C 批评系列:继承的本质

2008-02-23 05:25:10来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

 Eiffel和C 都提供了多继承的机制。但Java却没有,因为他认为多继承会导致许多问题的出现。但是Java提供了接口(interface)作为一种替换机制,他类似于Objective C中的协议(protocol)。Sun宣称接口能够提供多继承所能提供的任何特性。

  Sun所宣称的“多继承会带来许多的问题”这个观点是对的,尤其是在C 中用以实现多继承的方法更能说明这一点。那些看起来似乎使用多继承会比单继承更简单的理由,现在都以被证实是毫无意义。例如,如何制订对于从两个类之上继承得到的具备相同名字的数据项之间的策略?他们之间是否兼容?假如是的话,那他们是否应该被合并成为一个实体?假如不兼容,那应该如何区分他们?……这样的列表能够列出很长很长。

  Java的接口机制也能够用以实现多继承,但他也有一个很重要的不同之处(和C 相比):继承中的接口必须是抽象的。由于使用接口并没有任何的实作,这就消除了需要从不同实作之间选择的可能。Java允许在接口中声明具备常数字段。当需要多继承时,他们就合并成为一个实体,这样也就不会导致歧义的产生。但是,当这些常数具备不同的值时,又有什么会发生呢?

  由于Java不支持多继承,我们就不能够像在C 和Eiffel中那样使用混合(mixin)了。混合是一种特性,他能够把从不同的类中得到的不同的非抽象的函数放到一起形成一个新的复杂的类。例如,我们可能希望从不同的源代码中导入一些utility函数。然而,我们也能够通过使用组合而不是继承来达到同样的效果,因此,这也就不会对Java构成一个重要的攻击了。

  Eiffel在解决多继承问题时并没有导入一个单独的接口机制。

  有些人可能认为,相对于多继承来说,单继承更优雅一些。这是个很特别的观点。

  BETA [Madsen 93]就属于认为“多继承不优雅”的那一种:“Beta中没有多继承,这主要是因为(对于多继承)缺乏一个深刻的理论上的理解,并且当前的(对于多继承的)建议在技术上看来也很复杂”。他们引用了Flavors(一种能够将类混合在一起的语言)为证据。和Madsen相比,Flavors中的多继承和其顺序有关,也就是说,继承自(A,B)和继承自(B,A)是不相同的。

  Ada95是另一种不支持多继承的语言。Ada95支持单继承,并把他叫做标记类型扩展(tagged type extension)。

  另外一些人认为,对于某些特别模型下的问题,多继承能够提供优雅的解法,因此为之付出的努力也是值得的。虽然上面所列出的关于多继承的问题列表并不完善,他仍然显示:和多继承相关的问题是能够被系统地辨识出来的,而一旦问题被确认,他们也就能够被优雅地解决。当[Sakkinen 92]对于多继承研究到达一个很深的程度后,他就得出了上述定义。

  Eiffel中采用的方法是,多继承会引发一些有趣的且有挑战性的问题,然后再优雅地解决他们。程式员所需做的任何决定都被限制在类的继承子句中。他包括使用renaming来确保众多从继承中得来的同名特性最终成为具备不同名字的特性,对于继承而来的特性所施展的新的export策略:redefining和undefining,连同用来消除歧义的select。在任何的情况下,编译器都会为我们做好这一切,为了使得语义清楚而不管是选择使用fork或是join,程式员都具备完全的控制权。

  C 中相对Eiffel来说有着另外一种不同的用于消除歧义的机制。在Eiffel中,在renames子句中,特性间必须有着不同的名字。在C 中,能够使用域解析操作符’::’来区分成员。Eiffel的做法好处在于,歧义在声明中就被消除掉了。Eiffel的继承子句相对C 的来说要复杂不少,但他的代码也显得更简单,更稳固,并更具弹性。这也就是声明方法和操作符方法相比的好处所在。在C 中,每次当我们碰到在多个成员间具备歧义时,我们必须在代码中使用域解析操作符。这经使得代码变得混乱不堪,影响其延展性,假如有其他地方的改变会影响歧义时,我们可能就需要在歧义可能出现的每个地方改变已有的代码。

  依照[Stroustrup 94]中12.8节所说,ANSI委员会考虑过使用renaming,但是这个提议被委员会中的一个成员所阻塞掉了,他坚持让委员会中的其他成员用两周时间来好好地考虑这个问题。在12.8节中给出的例子显示了在没有显示的renaming的前提下,如何做能够得到同样的效果。问题在于,假如这都需要那些专家们使用两周来考虑如何实现,那留给我们的空间又有多少呢?

  域解析操作符并不只是被用来消除多继承所带来的歧义。由于设计良好的语言能够避免歧义的出现,因此域解析操作符也就是个丑陋的,加深复杂性的实作手法。

  在C 中,“如何来声明多继承中的父类们”是个很复杂的问题。他影响到了建构函数被调用的次序,当程式员确实想从子类转到父类时也会导致问题的出现。然而,我们也能够把这个称为不好的程式设计风格。

  C 和Eiffel的另一个不同之处在于直接的重复继承,Eiffel中允许:

 class B inherit A, A end   但

  class B : public A, public A { };

  却不被C 认可。




标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇: 详述C 语言的VxD和外界通讯的任何接口

下一篇: C 指针使用方法解惑

热门词条
热门标签