More Exceptional C++中文版试读(继承与多态)

[Herb Sutter 的名作 More Exceptional C++ 中文版即将出版。作为本书译者,我很高兴将本书推荐给大家。征得华中科技大学出版社同意,我将公开部分译稿,敬请大家批评指正。

 

  

继承与多态

 

 

 

不来点继承和多态,面向对象将会怎样?

 

尽管继承常被滥用,但它还是一种很重要的工具——这包括多继承。特别是,当你生活在现实世界中时,你会发现,你经常需要将不同供货商提供的程序库结合起来使用,此时,多继承便凸显它的价值。本章向你展示,在结合使用不同供货商提供的“基于继承”的程序库时,应当如何避免连体双婴(Siamese Twin)问题。此外,本章还示范了许多合理(以及一些不合理地)使用纯虚函数的方式,以及如何编写多继承的替代方案、如何对使用继承关系的用户施加控制。

 

 

条款24:为什么要使用多继承?

难度:6

一些语言,包括SQL99标准,还在为“是否应该支持单继承或多继承”的问题大伤脑筋。本条款邀请您讨论这一主题。

 

1. 什么是多继承(MI,即multiple inheritance)?在C++中引入MI带来了哪些额外的可能性和复杂性?

 

2. MI究竟有必要吗?如果必要,尽可能多地列举出它的使用场合,并论证为什么应该将MI加入到语言中;如果不必要,请论证为什么单继承(SI)(并且,可能结合Java风格的接口)可以取代多继承、甚至比它更出色,以及为什么不应该将MI加入到语言中。

 

解答

 

1. 什么是多继承(MI,即multiple inheritance)?在C++中引入MI带来了哪些额外的可能性和复杂性?


非常简要地回答:MI指的是从多个(多于一个)直接基类(direct class)继承的能力。

 

例如:

 

    class Derived : public Base1, private Base2

    {

      //...

    };

 

C++中引入MI所带来的可能性是:一个类的同一个(直接或间接)基类(base class)可能会不只一次地作为它的基础类(ancestor)出现。这里有一个简单的例子,即那个经典的钻石形状的继承图,如图4所示。

 

这里,B两次作为D的间接基类(indirect class)出现,一次是通过C1,另一次则是通过C2

 

这种情况下,就很有必要引入C++的另一个特性:虚拟继承。现在的问题是:程序员希望D拥有基类B的一个子对象还是两个?如果答案是一个,B就应该是一个虚拟基类,图4就成为了可怕的死亡钻石。如果答案是两个,B就应该是一个普通(非虚拟)基类。

 

最后,虚拟基类的主要复杂性在于:它们必须通过最底层的派生类(most-derived class)直接初始化。关于这一点的详细介绍,以及MI其它方面的知识,请参阅[stroustrup00],或[Meyers97]条款43

 

B

D

C1

C2

 

 

 

 

 

 

 

 

 

 

 


4可怕的死亡钻石(如果DB虚拟继承)

 

 


 

设计准则

避免多继承自多个“非protocol类”。(protocol类是一种抽象基类(abstract base class),或简称ABC,它完全由纯虚函数组成,没有数据成员。)

 

2. MI究竟必要吗?

 

简短的回答是:只要程序可以用汇编语言(或更低级的语言)来写,就不能说某种特性绝对“必要”。然而,正如大多数人不会去用简单的C编写自己的虚函数机制一样,在某些场合下,没有MI会让你大费周章。

正因为如此,我们现在才有了这一被称为MI的神奇特性。但问题是——或者,至少前面的问题应该换为——MI是个好东西吗?1

简而言之,的确有人认为MI是个坏主意,因而无论如何都应该避免它。这不对。是的,如果你在使用MI时有欠考虑,它的确会招致不必要的耦合性和复杂性。然而,任何一种被误用的继承都是这样(参见Exceptional C++ [Sutter00]条款24),但我相信,大家不会就此认为继承不是个好东西。还有,是的,在完全不使用MI的情况下,任何程序都可以写出来,但你也得知道,在完全不使用继承的情况下,任何程序也都可以写出来。实际上,任何程序都可以用汇编语言来写。但这并不是说用汇编语言来写程序就一定是个好主意,相反,你甚至不愿意去做那样的事。

2.如果必要,尽可能多地列举出它的使用场合,并论证为什么应该将MI加入到语言中;如果不必要,请论证为什么单继承(SI)(并且,可能结合Java风格的接口)可以取代多继承、甚至比它更出色,以及为什么不应该将MI加入到语言中。

 

那么,什么时候使用MI才算合适?简而言之,只有在每一个继承单独取出来看都合适的时候,这样的MI才算合适。Exceptional C++条款24提供了一个相当详尽的列表,说明了什么时候该使用继承。在现实世界中,MI的应用大多逃不出以下三类。

1. 结合使用程序模块或程序库。之所以首先提出这一点有一个理由,我将在后面说明。很多类被设计为基类——即,在使用时你得从它继承。这很自然地带来一个问题:如果你想写一个类,它用到了两个程序库,但每个程序库都要求你从它的某个类继承,这时候你该怎么办?

__________________

1. 一定程度上,本条款的主题启发自19986SQL标准会议上发生的事件,在那次会议中,多继承被从ANSI SQL99标准草案中删除了。(如果说得远一点的话,你们当中如果有人对数据库感兴趣,就会知道,在SQL4MI又以修订形式重新恢复了。)当时之所以那样做,主要是因为所提出的多继承规范存在技术上的困难,并且是为了想和Java这些不真正支持多继承的语言看齐。另外,仅仅是坐在那儿听人们在那样一个相对太迟的时间里争论多继承的优缺点,这本身就让人觉得有趣。在C++世界中,自这种语言形成以来,我们很少做这种事;这让我回忆起几年前(或比这更近的时期)新闻组上广泛展开的激烈争论,我会忍不住自言自语地念出一些标题,譬如:“MI是罪恶!!!”


在面对这种情况时,你一般无法通过修改程序库代码来避免某个继承。因为,它可能是从第三方供货商购买的程序库,或者,可能是你的公司里另一个项目组开发的模块。无论哪种情况,你不但不能修改代码,你甚至可能没有代码!如果是这样,MI就是必要的;没有其它(自然的)方法可以帮助你去做你必须做的事;此时,使用MI完全合理。

 

在实践中我发现,知道如何运用MI来结合使用供应商提供的程序库,这是每一个C++程序员必须具备的素质。无论你是否经常运用它,你绝对应该知道它并理解它。

 

2. Protocol类(interface类)。在C++中,MI最合适、最安全的应用是定义protocol 类——即,完全由纯虚函数构成的类。由于这种基类没有数据成员,MI臭名昭著的复杂性就得以完全避免。

 

有趣的是,有的语言(或模型)通过非继承机制来支持这种MIJavaCOM就是两个例子。严格来说,Java有多继承,但它将实现(implementation)的继承仅限于单继承。一个Java类可以实现多个“接口”,这种接口非常类似C++中没有数据成员的纯抽象基类。COM本身没有继承的概念(虽然在用C++来写COM对象时,这是一种常用的实现技术),但它同样也有“接口组合(composition of inerfaces)”的思想,COM接口类似Java接口和C++模板的结合。

 

3. 易于(多态)使用。有了继承,在接受基类对象的任何代码中,我们就都可以使用派生对象,这是一项威力强大的功能。在某些场合,如果同一个派生对象可以代替数种基类对象使用,那将会很有用处,这正是MI大显身手的地方。关于这一点有一个好例子,请参阅[Stroustrup00]14.2.2节,那里演示了一个基于MI的设计,用以构造异常类(exception class);在那个设计中,最底层的派生异常类可能和多个直接基类具有多态式的Is-A关系。

 

注意,第3点在很大程度上与第12点重叠。在实施另两点之一时,同时、并出于相同的理由运用第3点,往往很有用处。

 

还要考虑到另一点,不要忘记:有时候,单纯从两个不同的基类继承并没有必要,相反,我们要让每一个继承都有不同的理由。“多态的LSP Is-A 公有继承”并非唯一故事(译注:参见条款23);使用继承还有很多其它可能的原因。例如,一个类可能需要从一个基类A私有继承,以获得对类A的保护成员的访问权,同时,它还要从另一个基类B公有继承,以多态地实现类B的某个虚函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: exceptional c指的是超乎寻常的C语言,即在使用C语言进行编程时,达到了非凡的水平和能力。这包括但不限于熟练掌握C语言的语法和特性,理解C语言的底层机制和内存管理,编写高效且可靠的代码,解决复杂而困难的问题。拥有exceptional c能力的程序员通常能够编写高性能的程序,优化代码,改善资源利用效率,提高软件的质量和可维护性。 而more exceptional c则进一步指的是在exceptional c的基础上,更进一步的提升和发展。它可能包括对C语言更深层次的理解,对C语言编程范式、设计模式和最佳实践的掌握,能够设计和实现复杂的数据结构和算法,以及利用C语言的扩展功能和库实现创新的解决方案。 与exceptional c相比,more exceptional c更注重在C语言的高级特性上的应用,如面向对象的编程,泛型编程,元编程等。通过更深入的学习和研究,更出色的C程序员可以利用这些高级技术,进一步提高代码的可读性,可维护性和重用性。 总而言之,exceptional c是对熟练掌握C语言编程的基本要求,而more exceptional c则代表在此基础上进一步提升和发展,拥有更高级的技能和能力。 ### 回答2: exceptional c是指非常优秀的C语言代码,具有出色的设计和实现,能够在效率、可靠性和可读性等方面表现出色。这种代码通常具有良好的算法和数据结构选择,以及正确的错误处理机制。 而more exceptional c是指比exceptional c更加出色的C语言代码,更具有创新和突破性。它可能包含了前沿的编程技术、高级的数据结构和算法,以及更为复杂和深入的问题解决方案。这种代码可能会利用C语言的底层优势,充分发挥计算机性能并提供更高的效率。 在开发过程中,编写exceptional c代码需要遵循良好的编程习惯和规范,保持代码的可维护性和可扩展性。这种代码通常具有清晰的结构和注释,函数和变量命名规范恰当。同时,编写exceptional c代码也要注意错误处理和异常处理,确保代码的健壮性和可靠性。 而more exceptional c则需要更深入的编程知识和技巧。它可能涉及更多的底层编程,例如操作系统级别的代码或者底层硬件的访问。同时,more exceptional c还可能包含更复杂的算法和数据结构,以及高级的设计模式和架构思想。编写more exceptional c代码需要对C语言的特性和底层工作原理有深刻的理解和掌握。 总之,exceptional c和more exceptional c都是指在C语言中表现出色的代码,但more exceptional c更进一步,采用更高级的技术和解决方案,展现出更出色的能力和创新精神。 ### 回答3: exceptional c是指非常出色的c,可能是指一个人的能力或者品质在某个方面超越了常人,表现出非凡的才华或者特点。它强调了c的某种特别之处,使得它在众多同类中脱颖而出。 而more exceptional c则意味着更加出色的c,即在exceptional c的基础上进一步提升,突破了先前的界限,表现出更加突出的表现和才能。它可以指一个人在原有的基础上通过努力和不断学习进步,取得了更加卓越的成就,或者是指一个事物在原有的基础上通过创新和改进,达到了更高的水平。 总之,exceptional c和more exceptional c都表达了某种非凡和出色的意思,但more exceptional c更加强调在原有的基础上取得更高的成就和突破。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值