C++编程批评系列 继承的本质

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++认可。

时间: 2024-09-08 12:48:04

C++编程批评系列 继承的本质的相关文章

Windows 核心编程研究系列之一(-改变进程PTE属性-)[已补完]

  Windows 核心编程研究系列之一 -改 变 进 程 PTE 属性-              这是我研究windows 核心编程的第一篇正式文章,之所以叫核心编程而不叫内核编程,是我觉得从字面上来看核心(core)比内核(kernel)更靠近windows中心,当然只是偶本人的看法的拉.          我们知道在 win NT 中,系统把每个进程的虚拟4G空间分为两大部份,低2G归用户所有,高2G归系统所有.用户不得访问系统的空间,连读都不行,更别说写了!低2G的用户空间也并不是都能

适配器-为什么在GUI编程中要继承JFrame

问题描述 为什么在GUI编程中要继承JFrame 不能直接new一个窗体吗,比如这样:JFrame jf = new JFrame("图形"); 而且继承了窗体,就没办法继承适配器了. 看代码,像第二个版本那样有什么好处: //版本1 public class TestEventA { private JFrame jf; private JButton button; private JTextArea jta; public TestEventA() { jf = new JFra

Android编程根据系列图片绘制动画实例总结_Android

本文实例讲述了Android编程根据系列图片绘制动画的方法.分享给大家供大家参考,具体如下: 一.采用系统提供的Animation类,用自带的方法 其中的animation.xml文件如下: <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@

Android编程根据系列图片绘制动画实例总结

本文实例讲述了Android编程根据系列图片绘制动画的方法.分享给大家供大家参考,具体如下: 一.采用系统提供的Animation类,用自带的方法 其中的animation.xml文件如下: <animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"> <item android:drawable="@

JavaScript 面向对象编程, 第一部分: 继承

javascript|编程|对象|继承 我们将向你展示 JavaScript 如何实现面向对象的语言中的: 继承. 同时, 这些例子将向你展示如何实现类的封装. 在此, 我们不会讨论多态实现. 虽然 JavaScript 是脚本语言, 但它所支持的面向对象编程也是非常强大的. 虽然它没有类和实例, 但它有对象, 原型和隐式的继承. 我们将会解释如何模拟继承及其超类与子类之间关系的形式. 原型是理解继承概念的关键, 我们将会教你如何建立原型, 如何检测一个对象是否是另外一个对象的原型, 及其 Ja

用 eric6 与 PyQt5 实现python的极速GUI编程(系列01)--Hello world!

  [题记] 我是一个菜鸟,这个系列是我的学习笔记. PyQt5 出来有一段时间了, PyQt5 较之 PyQt4 有一些变化,而网上流传的几乎都是 PyQt4 的教程,照搬的话大多会出错. eric6 也是刚刚出来,eric6 与 PyQt5 结合的教程网上几乎没有. 本人也是一边学习,一边摸索,并记录与此,希望帮到有需要的朋友,更希望路过的大侠多多指教为好.   [引子] 用 eric6 与 PyQt5 结合,非常方便的实现界面与逻辑分离,满足python的极速GUI编程,你只需要关注程序的

澄清Java语言接口与继承的本质

继承 计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉.今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个--唉. 大多数人认为,接口的意义在于顶替多重继承.众所周知Java没有c++那样多重继承的机制,但是却能够实作多个接口.其实这样做是很牵

Java接口与继承的本质

计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉.今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个--唉. 大多数人认为,接口的意义在于顶替多重继承.众所周知Java没有c++那样多重继承的机制,但是却能够实作多个接口.其实这样做是很牵强的,

Java语言接口与继承的本质

  计算机学院研二的兄弟与我讨论Java,一见面,几个问题全是关于接口,接口有什么用?为什么要用接口?什么时候该使用接口?很庆幸他们不是问我Java如何连接SQL Server,或者是如何开发J2EE应用,这类问题有杀伤力,避之则吉.今年计算机学院本科有个毕业设计课题是做J2ME,选这个题目的学生在5月末都还在苦着脸研究java.util.*这个包,这个这个--唉. 大多数人认为,接口的意义在于顶替多重继承.众所周知Java没有c++那样多重继承的机制,但是却能够实作多个接口.其实这样做是很牵强