《计算机二级C++辅导面向对象语言概论》由会员分享,可在线阅读,更多相关《计算机二级C++辅导面向对象语言概论(4页珍藏版)》请在金锄头文库上搜索。
1、计算机二级C+辅导 : 面向对象语言概论(二)Subsumption 和 Dynamic Dispatch从上述的几个例子来看,似乎子类只是用来从父类借用一些定义,以避免重复。但是,当我们考虑到 subsumption, 事情就有些不同了。什么是 Subsumption 呢?请看下面这个例子:var myCell: InstanceTypeOf(cell) := new cell;var myReCell: InstanceTypeOf(reCell) := new reCell;procedure f(x: InstanceTypeOf(cell) is . end;再看下面这段代码:myC
2、ell := myReCell;f(myReCell);在这两行代码中,头一行把一个InstanceTypeOf(reCell)类型的变量赋值给一个InstanceTypeOf(cell)的变量。而第二行则用InstanceTypeOf(reCell)类型的变量作为参数传递给一个参数类型为InstanceTypeOf(cell)的函数。这种用法在类似Pascal 的语言中是不合法的。 而在面向对象的语言中,依据以下的规则,它则是完全正确的用法。该规则通常被叫做subtype polimorphism,即子类型多态 (译者按:其实 subtyping应该是 OO语言最区别于其它语言的地方了)如果
3、 c 是 c 的子类,并且o 是 c 的一个实例,那么o 也是 c 的一个实例。更严格地说:如果 c 是 c 的子类,并且o: InstanceTypeOf(c),那么 o: InstanceTypeOf( c ).仔细分析上面这条规则,我们可以在InstanceTypeOf的类型之间引入一个满足自反和传递性的子类型关系,我们用那么上面这条规则可以被拆成两条规则:1 对任何 a: A,如果 A2InstanceTypeOf(c)第一条规则被叫做Subsumption.它是判断子类型 (注意, 是 subtype,不是 subclass )的标准。第二条规则可以叫做subclassing-is-
4、subtyping (子类就是子类型,绕嘴吧?)一 般 来 说 , 继 承 都 是 和subclassing相 关 的 , 所 以 这 条 规 则 也 可 以 叫 做 :inheritance-is-subtyping (继承就是子类型)所有的面向对象语言都支持subsumption( 可以说,没有 subsumption,就不成为面向对象 ) 。大部分的基于类的面向对象语言也并不区分subclassing和 subtyping.但是,一些最新的面向对象语言则采取了把subtyping和 subclassing分开的方法。也就是说,A 是 B 的子类,但A 类的对象却不可以当作B 类的对象来使
5、用。 (译者按:有点象C+里的私有继承,但内容比它丰富)好吧,关于区分subclassing和 subtyping,我们后面会讲到。下面,让我们重新回头来看看这个procedure f.在 subsumption的情况下,下面这个代码的动态语义是什么呢?Procedure f(x: InstanceTypeOf(cell) isx.set(3);end;f(myReCell);当 myReCell 被当作 InstanceTypeOf(cell)的对象传入f 的时候, x.set(3)究竟是调用哪一个版本的set 方法呢?是定义在cell中的那个set 还是定义在这时,我们有两种选择,1 St
6、atic dispatch (按照编译时的类型来决定)2 Dynamic dispatch (按照对象运行时真正类型来决定)reCell中的那个呢?(译者按,熟悉C+的朋友们一定微笑了,这再简单不过了。)static dispatch没什么可说的。dynamic dispatch却有一个有趣的属性。那就是,subsumption一定不能影响对象的状态。如果你在subsumption的时候,改变了这个对象的状态,比如象C+中的对象切片,那么动态解析的方法就可能会失败。好在,这个属性无论对语义,还是对效率,都是很有好处的。(译者按, C+中的 objectslicing会把新的对象的vptr初始化
7、成它自己类型的vtable指针 ,所以不存在动态解析的问题。但实际上,对象切片根本不能叫做subsumption 。具体语言实现中,如C+, 虽然 subsumption不会改变对象内部的状态,但指针的值却是可能会变化的。这也是一个让人讨厌的东西,但C+ vtable的方案却只能这样。有一种变种的 vtable方法,可以避免指针的变化, 也更高效。我们会在另外的文章中阐述这种方法。赛翁失马(关于类型信息)虽然 subsumption并不改变对象的状态,在一些语言里(如Java ),它甚至没有任何运行时开销。但是,它却使我们丢掉了一些静态的类型信息。比如说,我们有一个类型InstanceType
8、Of(Object),而 Object类里没有定义任何属性和方法。我们又有一个类MyObject,它继承自Object 。那么当我们把MyObject 的对象当作)InstanceTypeOf(Object)类型来处理的时候,我们就得到了一个什么东西也没有的没用的空对象。当然,如果我们考虑一个不那么极端的情况,比如说, Object类里面定义了一个方法而 MyObject 对方法 f 做了重载,那么,通过 dynamic dispatch,我们还是可以间接地操作f,MyObject 中的属性和方法的。这也是面向对象设计和编程的典型方法。从一个 purist的角度看(译者按,很不幸,我就是一个p
9、urist) , dynamic dispatch是你应该用来操作已经被subsumption忘掉的属性和方法的东西。它优雅,安全,所有的荣耀都归于dynamic dispatch!不过,让 purist们失望的是, 大部分语言还是提供了一些在运行时检查对象类型,并从而操作被subsumption遗忘的属性和方法。这种方法一般被叫做RTTI( Run Time TypeIdentification)。如 C+中的 dynamic_cast,或 Java 中的 instanceof.实事求是地说, RTTI 是有用的。(译者按, 典型的存在就是合理的强盗逻辑,气死我了!)。但因为一些理论上以及方
10、法论上的原因,它被认为是破坏了面向对象的纯洁性。首先,它破坏了抽象,使一些本来不应该被使用的方法和属性被不正确地使用。其次,因为运行时类型的不确定性,它有效地把程序变得更脆弱。第三点, 也许是最重要的一点,它使你的程序缺乏扩展性。当你加入了一个新的类型时,你也许需要仔细阅读你的dynamic_cast或 instanceof的代码,必要时改动它们,以保证这个新的类型的加入不会导致问题。很多人一提到RTTI,总是侧重于它的运行时的开销。但是,相比于方法论上的缺点,这点运行时的开销真是无足轻重的。而在 purist的框架中(译者按,吸一口气,目视远方,做深沉状),新的子类的加入并不需要改动已有的代
11、码。这是一个非常好的优点,尤其是当你并不拥有全部源代码时。总的来说,虽然RTTI (也叫 type case)似乎是不可避免的一种特性,但因为它的方法论上的一些缺点,它必须被非常谨慎的使用。今天面向对象语言的类型系统中的很多东西就是产生于避免RTTI 的各种努力。比如有些复杂的类型系统中可以在参数和返回值上使用Self类型来避免RTTI.这点我们后面会介绍到。协变,反协变和压根儿不变(Covarance, Contravariance and Invariance)在下面的几个小节里,我们来介绍一种避免RTTI 的类型技术。 在此之前, 我们先来介绍 协变 , 反协变 和 压根儿不变 的概念。
12、协变首先,让我们来看一个 Pair 类型: A*B这个类型支持一个 getA() 的操作以返回这个 Pair 中的 A 元素。给定一个 A为什么呢?我们可以用 Subsumption 的属性加以证明 :假设我们有一个A*B类型的对象 a*b,这里, a:A, b:B, a*b那么,因为, A这样,我们就定义A*B 这个类型对于 A 是协变的。同理,我们也可以证明A*B 对于 B 也是协变的。正规一点说, Covariance 是这样定义的:给定 L(T), 这里,类型 L 是通过类型 T 组合成的。那么,如果 T1反协变请看一个函数:A f(Bb); ( 用 functionallanguag
13、e 的定义也许更简洁,即 f: B-A)那么,给定一个B A和 B-A 之间有什么样的 subtype 关系呢?可以证明, B-A A 。基于篇幅,我们不再做推导。所以,函数的参数类型是反协变的。Contravariance的正规点的定义是这样的:给定 L(T), 这里,类型 L 是通过类型 T 组合成的。那么,如果 T1同样,可以证明,函数的返回类型是协变的。压根儿不变那么我们再考虑函数g: A-A这里, A 既出现在参数的位置,又出现在返回的位置,可以证明,它既不是协变的,也不是反协变的。对于这种既不是协变的,也不是反协变的情况,我们称之为Invariance (译者按: 压根儿不变 是我编的,这么老土的翻译,各位不必当真)值得注意的是,对于第一个例子中的Pair类型,如果我们支持setA(A),那么, Pair就变成 Invariance了。方法特化( Method Specialization)在我们前面对subclass的讨论中, 我们采取了一种最简单的override的规则,那就是,overriding的方法必须和overr