剖析C++5种继承模型
2017-07-25 09:48
239 查看
剖析C++5种继承模型
单继承
若一个子类(派生类)只有一个父类(基类)时,叫单继承。 若一个子类(派生类)有有多个父类(派生类)时,叫多继承。
先来看看单继承模型
1.class C 2.{ 3.public: 4. C() 5. { 6. cout<<"C()"<<endl; 7. } 8. 9. int _c1; 10.}; 11. 12.class B: public C 13.{ 14.public: 15. B() 16. { 17. cout<<"B()"<<endl; 18. } 19. 20. int _b1; 21. 22.}; 23. 24.void test() 25.{ 26. B b; 27. b._b1 = 10; 28. b._c1 = 20; 29.}
C类中有自己的构造函数和成员变量_c1,B类是公有继承C类的,B类中也有自己的构造函数和成员变量,如果给_c1 和 _b1分别赋值,在调用的时候是先调用哪一个呢?
打开内存窗口,对构造出来的对象取地址可以看到,先赋值的是_c1的值,后赋值_b1的
所以,在单继承模型中,先调用的是基类的成员变量,然后是派生类的
多继承模型
一个子类的继承对象可能有很多,如果基类有两个及以上就叫多继承,一般格式为 派生类 : 继承方式 基类1,继承方式 基类2
1.class C1 2.{ 3.public: 4. C1() 5. { 6. cout<<"C1()"<<endl; 7. } 8. 9. int _c1; 10.}; 11. 12.class C2 13.{ 14.public: 15. C2() 16. { 17. cout<<"C2()"<<endl; 18. } 19. 20. int _c2; 21.}; 22. 23. 24.class B: public C1, public C2 25.{ 26.public: 27. B() 28. { 29. cout<<"B()"<<endl; 30. } 31. 32. int _b1; 33. 34.}; 35. 36.void test() 37.{ 38. B b; 39. b._b1 = 10; 40. b._c1 = 20; 41. b._c2 = 30; 42.} 43.
对于B类,是继承C1和C2的,继承顺序为先继承C1,然后再继承C2,继承方式都为公有继承,先来看看,如果分别给三个类里的成员变量赋值,通过派生类能得到什么样的继承顺序
打开内存窗口,用调试的方式,往下看可以看到先在最上面调用的是顺序在前的基类变量,然后是基类2,最后才是派生类的变量,所以在多继承模型中,最上层是基类1,然后是基类2,最后才是派生类
菱形继承(钻石继承)
在多继承中,派生类的基类有两个,所以叫多继承。如果现在多继承中的基类同时继承一个相同的基类,那么这种继承就叫做菱形继承。也叫钻石继承1.class A 2.{ 3.public: 4. A() 5. { 6. cout<<"A()"<<endl; 7. } 8. 9. int _a1; 10.}; 11. 12. 13.class C1: public A 14.{ 15.public: 16. C1() 17. { 18. cout<<"C1()"<<endl; 19. } 20. 21. int _c1; 22.}; 23. 24.class C2: public A 25.{ 26.public: 27. C2() 28. { 29. cout<<"C2()"<<endl; 30. } 31. 32. int _c2; 33.}; 34. 35. 36.class B: public C1, public C2 37.{ 38.public: 39. B() 40. { 41. cout<<"B()"<<endl; 42. } 43. 44. int _b1; 45. 46.};
现在B世纪城C1 和 C2 的, 而C1 C2 分别继承于A,每个类中都有自己独有的成员变量,现在通过派生类B给_b1,_c1,_c2,_a1,_a2分别赋值,然后编译一下,系统会报错,“对_a1的访问不明确”,这是因为,在B继承的C1和C2中都有继承A的成员变量,所以要对_a1进行赋值就必须加上类作用域限定符(例:b.C1::_a1 = 10)
这就是所谓的菱形继承二义性问题,在内存中,B继承下来的变量存储的位置是
菱形继承实际上就是每一个基类中都有上一层基类的变量,然后派生类把两个基类按继承顺序依次的继承下来。所以菱形继承的一般模型为
虚拟继承
为了解决菱形继承带来的二义性和数据冗余问题,就采取了一种方法:虚拟继承。 虚拟继承:在继承权限前面加上关键字virtual
1.class C1 2.{ 3.public: 4. C1() 5. { 6. cout<<"C1()"<<endl; 7. } 8. 9. int _c1; 10.}; 11. 12. 13.class B: virtual public C1 14.{ 15.public: 16. B() 17. { 18. cout<<"B()"<<endl; 19. } 20. 21. int _b1; 22. 23.}; 24. 25.void test() 26.{ 27. B b; 28. b._b1 = 10; 29. b._c1 = 20; 30.}
现在,让B虚拟继承C1,然后给C1和B里面的变量依次赋值,是不是还和单继承或多继承一样,基类的数据存放在前面。打开内存窗口,然后对b取地址,可以看到先是一串数字,然后是0a(派生类变量)再是14(基类变量),那第一行中的数字是什么意思?
这串数字占了4个字节,所以应该想到他是一个地址,那就好办。再打开一个内存窗口,将这个地址查看一下(注:VS里存储数据是小端存储)
进入这个地址中去,第一行是00 00 00 00 ,第二行是08 00 00 00 ,先来解释一下这两行的值代表什么含义。00 00 00 00 表示这块地址相对于自己的偏移量, 08 00 00 00 表示相对基类的偏移量。而这块表就叫做偏移量表格。所以对于一般的虚拟继承都有以下的模型来解释他
菱形虚拟继承
在刚刚的菱形继承模型中,最后发现会存在二义性和数据冗余的问题,而虚拟继承不用存储基类的基类的值,改为了存偏移量表格,然后将基类放在最下面,这就消除了二义性问题,那么用虚拟继承解决菱形继承的问题就叫做菱形虚拟继承1.class A 2.{ 3.public: 4. A() 5. { 6. cout<<"A()"<<endl; 7. } 8. 9. int _a1; 10.}; 11. 12. 13.class C1: virtual public A 14.{ 15.public: 16. C1() 17. { 18. cout<<"C1()"<<endl; 19. } 20. 21. int _c1; 22.}; 23. 24.class C2: virtual public A 25.{ 26.public: 27. C2() 28. { 29. cout<<"C2()"<<endl; 30. } 31. 32. int _c2; 33.}; 34. 35. 36.class B: public C1, public C2 37.{ 38.public: 39. B() 40. { 41. cout<<"B()"<<endl; 42. } 43. 44. int _b1; 45. 46.}; 47. 48.void test() 49.{ 50. B b; 51. b._b1 = 10; 52. b._c1 = 20; 53. b._c2 = 30; 54. b.C2::_a1 = 2; 55.} 56.
在菱形继承中,关键字virtual是在基类的上面加,而不是在派生类继承时加。
虚拟继承时,是在派生类里加一个指针,那么现在的菱形继承中指针是在C1还是C2处加。首先B肯定是在下面,现在C1和C2都继承于A,那么A就是C1和C2的下面,而A和B的顺序是什么样的呢?
打开内存窗口,分别给成员变量赋值之后可以看到A是在最下面的,而 a0 dc 19 01 和 ac dc 29 01 在这就应该是C1和C2的偏移量表格指针
再进一步查看一下指向偏移量表格的指针
果然,和虚拟继承一样,在偏移量表格里,都有相对自己的偏移量和相对基类的偏移量,按顺序继承下来之后,C1在最上面所以相对基类A的偏移量就是20,然后继承的是C2,相对基类A的偏移量就是12。而虚拟继承刚开始说是为了解决菱形继承带来的二义性问题,那么现在能够直接通过B给A里面的变量赋值吗?
编译之后没问题,这样菱形继承所带来的二义性问题也就得到解决了。再来看看菱形虚拟继承的一半模型
相关文章推荐
- C++多继承虚函数类内部模型结构剖析
- C++虚拟继承中的对象模型
- C++虚拟多重继承对象模型讨论
- C++继承时的对象模型
- C++ 虚继承的对象模型
- 虚继承和虚函数对c++对象存储模型的影响(类/对象的大小)
- [C++对象模型][8]多重继承与虚函数表
- [置顶] 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响(类/对象的大小)
- C++虚继承内存对象模型探讨
- 继承体系下C++对象模型.md
- C++中的【菱形虚继承】深入剖析
- C++虚函数、虚继承、对象内存模型(转)
- C++ 无虚函数、无虚基类的继承内存模型
- C++多重继承和虚拟继承对象模型、效率分析
- C++三大特性之多态(二)---深度剖析各种虚继承虚函数以及虚表的内容存放
- C++ 虚继承和虚函数同时存在的对象模型
- C++对象模型——"继承"与Data Member(第三章)
- [C++对象模型][8]多重继承与虚函数表
- 剖析C++对象模型
- 【C++深度剖析教程24】C++中不同的继承方式