剖析C++5种继承模型

剖析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里面的变量赋值吗?

 
编译之后没问题,这样菱形继承所带来的二义性问题也就得到解决了。再来看看菱形虚拟继承的一半模型


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值