菱形继承问题(钻石问题)

我看到网上有很多人都在说虚继承和虚表的关系,我一直很郁闷,虚继承和虚表没有什么太大的关系,虚表是当有虚函数出现的时候才会有的,光是使用虚继承是不会有虚表的!当然也就不会有虚表指针!!从我后面的截图可以看到使用虚继承对象的内存构造中并没有出现虚表指针!跟虚继承有关的是一个虚基类表(vbptr),这个表在调试的监视窗口是看不见的,但有了虚继承之后使用sizeof可以明显看出,类型所占内存的大小扩大了4个字节!

菱形继承问题(钻石问题)

况且问题就不一样好么?虚继承解决的是二义性的问题,而虚函数是为了解决多态的问题。

菱形继承问题(钻石问题)

仔细看过之后就会发现虚继承表和虚函数表不是一个东西,地址不一样存放的东西也不一样,上图中虚函数表地址是0x008c7b40虚继承表地址是0x008c7b48,虚函数表存放虚函数指针,虚继承表存放的是整型偏移量

在学习C++的时候,菱形继承问题绝对是一个不可避免的重点问题,那么什么是菱形继承问题呢?下图就是,长得像不像钻石?我画图确实很难看

菱形继承问题(钻石问题)

因为C++允许多继承,当继承关系像上图这样子的时候,就会出现这样子的情况

A类是基类,B里面有个A我表示为B(A),C里面有个A我表示为C(A)

那么D里面有B和C我表示为D(B(A)C(A))

当我们想去使用D里面的A的时候,或者说访问A的部分值,在说白了究竟哪个A才是属于D的,D中的A究竟是B的A还是C的A?

1 class A 2 {}; 3 class B :public A 4 {}; 5 class C :public A 6 {}; 7 class D :public B, public C 8 {};

这么写可就错了,有的编译器甚至都不让你通过,直接给你报错

这很令人尴尬不是么,就算编译期让你通过了,也不要试图这样去通过D的对象访问其内部的A对象,这会让编译器很纠结

但是很简单给个vitual就好了

1 class A 2 {}; 3 class B :virtual public A 4 {}; 5 class C :virtual public A 6 {}; 7 class D : public B, public C 8 {};

菱形继承问题(钻石问题)

他有了一个属于自己的A,调用A中的变量或者函数的时候就会去属于自己的A中调用,就不会让编译期纠结了

那么当我们想要通过D类对象访问D类对象内部的B中的A类部分呢?

因为继承的关系C的A和B的A应该是一样的(因为都在D类对象中的关系),访问到D中B的A部分之后,转而去调用d本身的A

让我们给每个类都加上一个成员变量,从内存的角度看这个问题

类结构改成A:_a   B:_b   C:_c   D:_d,表示各个类独有的成员变量,虚继承关系不变

就会得到这样的结果

菱形继承问题(钻石问题)

你们有看到虚表指针一类的东西么?没有虚表指针_vptr!

也就是说,内存布局是这样子的

菱形继承问题(钻石问题)

我们查询一下d变量的内存地址,打开内存窗口看一下

菱形继承问题(钻石问题)

上图中最上面的两个红色区域就是d变量中B和C所占用的内存空间,每一行都是四个字节,因为我们已经知道没有被初始化的int变量的内存值用16进制表示就是cc cc cc cc所以在B和C中各自的第一行就是我们需要分别访问的A了,看着他们俩的A都不太对劲,不太像是一个对象的样子,我们把这两串神秘的值相减,结果为8

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/fb0d7b040bf2dd4e7e0cbf87f5f819b9.html