用于大型程序的工具

--多重继承与虚继承

引言:

大多数应用程序使用单个基类公用继承,可是,在某些情况下,单继承是不够用的,由于可能无法为问题域建模,或者会对模型带来不必要的复杂性。

在这些情况下,多重继承能够更直接地为应用程序建模多重继承是从多于一个直接基类派生类的能力,多重继承的派生类继承其全部父类的属性

一、多重继承

1、定义多个类

为了支持多重继承,扩充派生列表:

  1. class Bear : public ZooAnimal
  2. {
  3. //...
  4. };

以支持由逗号分隔的基类列表:

  1. class Panda : public Bear,public Endangered
  2. {
  3. //...
  4. };

派生类为每一个基类(显式或隐式地)指定了訪问级别——public、protected 或 private。像单继承一样,仅仅有在定义之后,类才干够用作多重继承的基类。对于类能够继承的基类的数目,没有语言强加的限制,但在一个给定派生列表中,一个基类仅仅能出现一次。

2、多重继承的派生类从每一个基类中继承状态
在多重继承下,派生类的对象包括每一个基类的基类子对象:

  1. Panda ying_ying("ying_ying");

对象ying_yang包括一个 Bear类子对象(Bear类子对象本身包括一个ZooAnimal基类子对象)、一个Endangered类子对象以及Panda类中声明的非static数据成员(假设有的话)。

3、派生类构造函数初始化全部基类

构造派生类型的对象包括构造和初始化它的全部基类子对象。像继承单个基类的情况一样,派生类的构造函数能够在构造函数初始化式中给零个或多个基类传递值:

  1. Panda::Panda(std::string name, bool onExhibit):
  2. Bear(name, onExhibit, "Panda"),
  3. Endangered(Endangered::critical) {}
  4.  
  5. Panda::Panda():
  6. Endangered(Endangered::critical) {}

4、构造次序

构造函数初始化式仅仅能控制用于初始化基类的值,不能控制基类的构造次序。基类构造函数依照基类构造函数在类派生列表中的出现次序调用。对Panda而言,基类初始化的次序是:

1)ZooAnimal,从Panda的直接基类Bear沿层次向上的终于基类。

2)Bear,第一个直接基类

3)Endangered,第二个直接基类,它本身没有基类。

4)Panda,初始化Panda本身的成员,然后执行它的构造函数的函数体。

【注解】

构造函数调用次序既不受构造函数初始化列表中出现的基类的影响,也不受基类在构造函数初始化列表中出现的次序的影响。

比如,在Panda类的默认构造函数中,隐式调用Bear类的默认构造函数,它不出如今构造函数初始化列表中,但仍在显式列出的Endangered类构造函数之前调用Bear类的默认构造函数

5、析构的次序

总是按构造函数执行的逆序调用析构函数。如:

  1. ~Panda, ~Endangered, ~Bear, ~ZooAnimal

二、转换与多个基类

在单个基类情况下,派生类的指针或引用能够自己主动转换为基类的指针或引用,对于多重继承也是如此,派生类的指针或引用能够转换为其随意基类的指针或引用。

  1. void print(const Bear &);
  2. void highlight(const Endangered &);
  3. ostream &operator<<(ostream &,const ZooAnimal &);
  4.  
  5. Panda ying_ying("ying_ying");
  6.  
  7. print(ying_ying);
  8. highlight(ying_ying);
  9. cout << ying_ying << endl;

在多重继承的情况下,遇到二义性转换的可能性更大。编译器不会试图依据派生类转换来差别基类间的转换,转换到每一个基类都一样好:

  1. void print(const Bear &);
  2. void print(const Endangered &);
  3.  
  4. Panda ying_ying("ying_ying");
  5.  
  6. print(ying_ying); //Error:ambiguous

  1. //P617 习题17.25
  2. class X
  3. {
  4. //...
  5. };
  6. class A
  7. {
  8. //...
  9. };
  10.  
  11. class B : public A
  12. {
  13. //...
  14. };
  15. class C : private B
  16. {
  17. //...
  18. };
  19.  
  20. class D : public X ,public C
  21. {
  22. //...
  23. };
  24.  
  25. D *pd = new D;
  26.  
  27. X *px = pd;
  28. B *pb = pd; //Error:注意C对B的继承是private!
  29. A *pa = pd; //Error
  30. C *pc = pd;

1、多重继承下的虚函数

假定我们的类定义了下表列出的虚成员:

ZooAnimal/Endangered类中的虚函数

函数

定义自己版本号的类

print

ZooAnimal::ZooAnimal

Bear::Bear

Endangered::Endangered

Panda::Panda

highlight

Endangered::Endangered

Panda::Panda

toes

Bear::Bear

Panda::Panda

cuddle

Panda::Panda

析构函数

ZooAnimal::ZooAnimal

Endangered::Endangered

2、基于指针类型或引用类型的查找

像单继承一样,用基类的指针或引用仅仅能訪问基类中定义(或继承)的成员,不能訪问派生类中引入的成员。

当一个类继承于多个基类的时候,那些基类之间没有隐含的关系,不同意使用一个基类的指针訪问其它基类的成员。

  1. Bear *pb = new Panda("ying_yang");
  2. pb->print(cout); // ok:參考上表
  3. pb->cuddle(); // error
  4. pb->highlight(); // error
  5. delete pb; // ok

在通过Endangered指针或引用訪问Panda对象时,不能訪问Panda接口的Panda特定的部分和Bear部分:

  1. Endangered *pe = new Panda("ying_yang");
  2. pe->print(cout); // ok
  3. pe->toes(); // error
  4. pe->cuddle(); // error
  5. pe->highlight(); // ok
  6. delete pe; // ok

3、确定使用哪个虚析构函数

假定全部根基类都将它们的析构函数适当定义为虚函数,那么,不管通过哪种指针类型删除对象,虚析构函数的处理都是一致的:

  1. //每一个指针都指向Panda对象
  2. delete pz;
  3. delete pb;
  4. delete pp;
  5. delete pe;

假定这些指针每一个都向Panda对象,则每种情况下发生全然同样的析构函数调用次序。析构函数调用的次序是构造函数次序的逆序:通过虚机制调用Panda析构函数。随着Panda析构函数的执行,依次调用Endangered、Bear和ZooAnimal的析构函数。

三、多重继承派生类的复制控制

多重继承的派生类的逐个成员初始化、赋值和析构,表现得与单继承下的一样,使用基类自己的复制构造函数、赋值操作符或析构函数隐式构造、赋值或撤销每一个基类。

假定Panda类使用默认复制控制成员。ling_ling的初始化

  1.  Panda ying_yang("ying_yang");
  2.  Panda ling_ling = ying_yang;

使用默认复制构造函数调用Bear复制构造函数,Bear复制构造函数依次在执行 Bear复制构造函数之前执行ZooAnimal复制构造函数。一旦构造了ling_ling的 Bear部分,就执行Endangered复制构造函数来创建对象的那个部分。最后,执行Panda复制构造函数。

合成的赋值操作符的行为相似于复制构造函数。

合成的析构函数撤销Panda对象的每一个成员,而且按构造次序的逆序为基类部分调用析构函数。

【小心地雷】

像单继承的情况一样,假设具有多个基类的类定义了自己的析构函数,该析构函数仅仅负责清除派生类。假设派生类定义了自己的复制构造函数或赋值操作符,则类负责复制(赋值)全部的基类子部分。仅仅有派生类使用复制构造函数或赋值操作符的合成版本号,才自己主动复制或赋值基类部分。

C++ Primer 学习笔记_95_用于大型程序的工具 --多重继承与虚继承的更多相关文章

  1. C++ Primer 学习笔记_88_用于大型程序的工具 --异常处理[续1]

    用于大型程序的工具 --异常处理[续1] 四.又一次抛出 有可能单个catch不能全然处理一个异常.在进行了一些校正行动之后,catch可能确定该异常必须由函数调用链中更上层的函数来处理,catch能 ...

  2. C++ Primer 学习笔记_87_用于大型程序的工具 --异常处理

    用于大型程序的工具 --异常处理 引言: C++语言包括的一些特征在问题比較复杂,非个人所能管理时最为实用.如:异常处理.命名空间和多重继承. 相对于小的程序猿团队所能开发的系统需求而言,大规模编程[ ...

  3. C++ Primer 学习笔记_91_用于大型程序的工具 --命名空间

    用于大型程序的工具 --命名空间 引言: 在一个给定作用域中定义的每一个名字在该作用域中必须是唯一的,对庞大.复杂的应用程序而言,这个要求可能难以满足.这样的应用程序的全局作用域中一般有很多名字定义. ...

  4. 【c++ Prime 学习笔记】第18章 用于大型程序的工具

    大规模应用程序的特殊要求包括: 在独立开发的子系统之间协同处理错误:异常处理 使用各种库(可能包含独立开发的库)进行协同开发:命名空间 对比较复杂的应用概念建模:多重继承 18.1 异常处理 异常处理 ...

  5. 【C++ Primer】用于大型程序的工具

    1. 异常处理 异常以类似于将实參传递给函数的方式抛出和捕获.异常可以是可传给非引用实參的随意实參的类型,这意味着必须可以复制该类型的对象. 当抛出一个表达式的时候,被抛出对象的静态编译时类型将决定异 ...

  6. C++ 用于大型程序的工具

    <C++ Primer 4th>读书笔记 相对于小的程序员团队所能开发的系统需求而言,大规模编程对程序设计语言的要求更高.大规模应用程序往往具有下列特殊要求: 1. 更严格的正常运转时间以 ...

  7. C++ Primer 5th 第18章 用于大型程序的工具

    C++大规模程序设计至少存在三个特殊要求: 错误处理 库的引入 复杂建模 以上三种对应C++语言的三种特性:异常处理.命名空间.多重继承. 异常处理 异常处理机制是一种允许偷懒的工具,在出现非正确的情 ...

  8. C/C++基础----用于大型程序的工具(异常处理,命名空间,多重继承)

    独立开发的子系统间协同处理错误的能力 使用各种库(可能包含独立开发的库进行协同开发的能力) 对比复杂的应用概念建模的能力 异常处理 异常将问题的检测和解决过程分离开 当执行一个throw之后,程序控制 ...

  9. C++ Primer学习笔记(二)

    题外话:一工作起来就没有大段的时间学习了,如何充分利用碎片时间是个好问题. 接  C++ Primer学习笔记(一)   27.与 vector 类型相比,数组的显著缺陷在于:数组的长度是固定的,无法 ...

随机推荐

  1. vim配色方案

    想更换vim配色方案,需要修改两个文件: 第一个修改是在 /user/share/vim/vim73/colors 添加xxx.vim文件://路径里面有些不是vim73,是vim70或其他 第二个修 ...

  2. Java多线程同步——生产者消费者问题

    这是马士兵老师的Java视频教程里的一个生产者消费者问题的模型 public class ProduceConsumer{ public static void main(String[] args) ...

  3. Android学习系列(7)--App轮询服务器消息

    这篇文章是android开发人员的必备知识. 1.轮询服务器     一般的应用,定时通知消息可以采用轮询的方法从服务器拿取消息,当然实时消息通知的话,建议采用推送服务.    其中需要注意轮询的频率 ...

  4. Android 数据传输之MessagePack使用

    介绍过什么是MessagePack之后,就进行Android与MessagePack的使用. 在MessagePack的官网上介绍MessagePack与Java结合使用的都是使用Maven作为JAR ...

  5. 《深入Java虚拟机学习笔记》- 第14章 浮点运算

    <深入Java虚拟机学习笔记>- 第13章 浮点运算

  6. 浅谈Javascript中默认参数值的设置

    第一种: 1: function test(a,b){ 2: var a = arguments[0] ? arguments[0] : 1;//设置参数a的默认值为1 3: var b = argu ...

  7. javascript设计模式5

    子类引用父类 function extend(subClass,superClass){ var F=function(){}; F.prototype=superClass.prototype; s ...

  8. [转载]mac下homebrew的使用

    该文转自:https://www.zybuluo.com/phper/note/87055 mac系统也是基于unix的系统,所以也继承类很多unix的特性,包括软件的编译,安装等.ubuntu下有快 ...

  9. 大数据架构师基础:hadoop家族,Cloudera产品系列等各种技术

    大数据我们都知道hadoop,可是还会各种各样的技术进入我们的视野:Spark,Storm,impala,让我们都反映不过来.为了能够更好的架构大数据项目,这里整理一下,供技术人员,项目经理,架构师选 ...

  10. SQL Server优化相关的工具脚本

    SQL Server性能优化的一些常用脚本,适用于SQL Server 2008,更高的版本某些系统表的字段有所不同,建议参考MSDN. 死锁相关 /************************* ...