多重继承与虚继承_第1页
多重继承与虚继承_第2页
多重继承与虚继承_第3页
多重继承与虚继承_第4页
多重继承与虚继承_第5页
已阅读5页,还剩3页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、 多重继承与虚继承分类: C+2011-07-07 13:50 0人阅读 评论(0) 收藏 举报转自:多重继承    在多重继承中,基类的构造函数的调用次序既不受派生类构造函数初始化列表中出现的基类构造函数的影响,也不受基类在构造函数初始化列表中的出现次序的影响,它按照基类在类派生列表中的出现次序依次调用相应的基类构造函数。析构顺序与构造顺序逆序进行。    多重继承中,派生类的指针或引用可以转换为其任意基类的指针或引用。因此,这种转换更可能遇到二义性问题。 

2、60;  在多重继承中,成员函数中使用的名字的查找首先在函数本身进行,如果不能在本地找到名字,就继续在成员的类中查找,然后同时(并行)查找所有基类继承子树。多重继承的派生类有可能从两个或多个基类继承同名成员,对该成员不加限定的使用是二义性的。    注意,多重继承中首先发生名字查找。你可能会感到吃惊的是,即使两个继承的同名函数有不同的形参表,也会产生错误。类似地,即使函数在一个类中是私有的而在另一个类中是公有或者受保护的,也是错误的。或者,在一个类给定义了函数,而在另一个类中没有定义,调用仍是错误的。例如: view plain1. #incl

3、ude <iostream>  2.   3. class Base1   4. public:  5.     void print()   6.     void display()   7.     void show()   8.

4、 ;  9.   10. class Base2   11. public:  12.     void print(int)   13.     void show(int);  14. private:  15.     void display(int) 

5、0; 16. ;  17.   18. class Derived: public Base1, public Base2   19. ;  20.   21. int main ()   22.     Derived d;  23.     d.print();&#

6、160; 24.     d.display();  25.     d.show();  26.       27.     return 0;  28.   view plain1. 编译结果(MinGW2.05):  2. Compiling source file(s).

7、  3. main.cpp  4. main.cpp: In function int main()':  5. main.cpp:24: error: request for member print' is ambiguous  6. main.cpp:13: error: candidates are: void Base2:print

8、(int)  7. main.cpp:6: error:                 void Base1:print()  8. main.cpp:25: error: request for member display' is ambiguous  9. mai

9、n.cpp:16: error: candidates are: void Base2:display(int)  10. main.cpp:7: error:                 void Base1:display()  11. main.cpp:26: error: request

10、 for member show' is ambiguous  12. main.cpp:14: error: candidates are: void Base2:show(int)  13. main.cpp:8: error:                 voi

11、d Base1:show()  14.   15. Test.exe - 9 error(s), 0 warning(s)      解决这种二义性的方法可以是通过指定使用哪个类的版本(即带上类名前缀)来解决。但最好的方法是,在解决二义性的派生类中定义函数的一个版本。 虚继承    在标准I/O库中的类都继承了一个共同的抽象基类ios,那个抽象基类管理流的条件状态并保存流所读写的缓冲区。istream和os

12、tream类直接继承这个公共基类,库定义了另一个名为isotream的类,它同时继承istream和ostream,iostream类既可以对流进行读又可以对流进行写。如果I/O类型使用常规继承,则每个iostream对象可能包含两个ios子对象:一个包含在它的istream子对象中,另一个包含在它的ostream子对象中。从设计角度讲,这个实现是错误的:iostream类想要对单个缓冲区进行读和写,它希望跨越输入和输出操作符共享条件状态。    在C+中,通过使用虚继承(virtual inheritance)解决这类问题。虚继承是一种机制,类通过虚继承指出它希

13、望共享其虚基类的状态。在虚继承下,对给定虚基类,无论该类在派生层次中作为虚基类出现多少次,只继承一个共享的基类子对象。共享的基类子对象称为虚基类(virtual base class)。    通过在派生类列表中包含关键字virtual设置虚基类,例如:   class istream : public virtual ios .;   class ostream : virtual public ios .;   class iostream : public istream, public ostre

14、am .;    假定通过多个派生路径继承名为X的成员,有下面三种可能性:   1)如果在每个路径中X表示同一虚基类成员,则没有二义性,因为共享该成员的单个实例;   2)如果在某个路径中X是虚基类的成员,而在另一路径中X是后代派生类的成员,也没有二义性特定派生类实例的优先级高于共享虚基类实例。   3)如果沿每个继承路径X表示后代派生类的不同成员,则该成员的直接访问是二义性的。    例如:view plain1. #include <iostream>

15、;  2.   3. class B   4. public:  5.     void print()   6.         std:cout << "B" << std:endl;  7.    &

16、#160;  8. ;  9.   10. class D1: public virtual B   11. ;  12.   13. class D2: public virtual B   14. public:  15.     void print()  

17、60;16.         std:cout << "D2" << std:endl;  17.       18. ;  19.   20. class DD: public D1, public D2   21. ; &

18、#160;22.   23. int main ()   24.     DD d;  25.     d.print();    / ok: call D2:print  26.       27.     return

19、0;0;  28.    特殊的初始化语义    通常,每个类只初始化自己的直接基类。在应用于虚基类的时候,这个初始化策略会失败。如果使用常规规则,就可能会多次初始化虚基类。类将沿着包含该虚基类的每个继承路径初始化。    为了解决这个重复初始化问题,从具有虚基类的类继承的类对初始化进行特殊处理。在虚派生中,由最低层派生类的构造函数初始化虚基类。    虽然由最低层派生类初始化虚基类,但是任何直接或间接继承虚基类的类一般也必须为该基类提供自己的初始化式。只

20、要可以创建虚基类派生类类型的独立对象,该类就必须初始化自己的虚基类,这些初始化式只在创建中间类型的对象时使用。    例如,我们有四个类:ZooAnimal, Bear, Raccoon和Panda,它们之间构造一个继承层次:Bear和Raccoon继承ZooAnimal,Panda继承Bear和Raccoon。那么,它们的构造函数就形如:   Bear:Bear(std:string name, bool onExhibit): ZooAnimal(name, onExhibit, "Bear")   

21、 Raccoon:Raccoon(std:string name, bool onExhibit): ZooAnimal(name, onExhibit, "Raccoon")    Panda:Panda(std:string name, bool onExhibit): ZooAnimal(name, onExhibit, "Panda"), Bear(name, onExhibit), Raccoon(name, onExhibit)     当创建Panda对象的时候,构造过程如下:&#

22、160;  1)首先使用构造函数初始化列表中指定的初始化式构造ZooAnimal部分;   2)接下来,构造Bear部分(在派生列表中Bear在前)。忽略Bear初始化列表中用于ZooAnimal构造函数的初始化式;   3)然后,构造Raccoon部分,再次忽略ZooAnimal初始化式;   4)最后,构造Panda部分。    如果Panda构造函数不显式初始化ZooAnimal基类,就使用ZooAnimal默认构造函数;如果ZooAnimal没有默认构造函数,则代码出错。 &#

23、160;  无论虚基类出现在继承层次中的任何地方,总是在构造非虚基类之前构造虚基类。    例如,有下面的继承关系:   class Character ;   class BookCharater: public Character ;   class ToyAnimal ;   class TeddyBear: public BookCharacter, public Bear, public virtual ToyAnimal ; 直观继承图为: &

24、#160; 按声明次序检查直接基类,确定是否存在虚基类。上例中,首先检查BookCharacter的继承子树,然后检查Bear的继承子树,最后检查ToyAnimal的继承子树。按从根类开始向下到最低层派生类的次序检查每个子树。在这里依次检查到ZooAnimal和ToyAnimal为虚基类。    TeddyBear的虚基类的构造次序是先ZooAnimal再ToyAnimal(检查到的顺序)。一旦构造了虚基类,就按声明次序调用非虚基类的构造函数:首先是BookCharacter,它导致调用Character构造函数,然后是Bear。在这里,由最低层派生类TeddyB

25、ear指定用于ZooAnimal和ToyAnimal的初始化式。    当然,对于析构函数的调用顺序与构造函数相反。    示例代码如下:view plain1. #include <iostream>  2.   3. class Character   4. public:  5.     Character()   6.  

26、60;      std:cout << "Character Constructor" << std:endl;  7.       8.     Character()   9.         std:cout

27、 << "Character Destructor" << std:endl;  10.       11. ;  12.   13. class BookCharacter: public Character   14. public:  15.     Book

28、Character()   16.         std:cout << "BookCharacter Constructor" << std:endl;  17.       18.     BookCharacter()   19. 

29、60;       std:cout << "BookCharacter Destructor" << std:endl;  20.       21. ;  22.   23. class ZooAnimal   24. public:  25. 

30、60;   ZooAnimal()   26.         std:cout << "ZooAnimal Constructor" << std:endl;  27.       28.     ZooAnimal()  

31、 29.         std:cout << "ZooAnimal Destructor" << std:endl;  30.       31. ;  32.   33. class Bear: public virtual ZooAnimal

32、60;  34. public:  35.     Bear()   36.         std:cout << "Bear Constructor" << std:endl;  37.       38.   

33、  Bear()   39.         std:cout << "Bear Destructor" << std:endl;  40.       41. ;  42.   43. class ToyAnimal  &#

34、160;44. public:  45.     ToyAnimal()   46.         std:cout << "ToyAnimal Constructor" << std:endl;  47.       48.   &

35、#160; ToyAnimal()   49.         std:cout << "ToyAnimal Destructor" << std:endl;  50.       51. ;  52.   53. class TeddyBear:

36、0;public BookCharacter, public Bear, public virtual ToyAnimal   54. public:  55.     TeddyBear()   56.         std:cout << "TeddyBear Constructor" << std:endl;  57.

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论