概念:
此时D类属于多继承,可以看到D类里面会有两份A类的数据,菱形继承也并不一定就一定就是上图的菱形,假如B类下面还有一个类,D类继承它,同样也是菱形继承问题
class A { public: int a; }; class B : public A { public: int b; }; class C : public A { public: int c; }; class D : public B, public C { public: int d; }; int main() { D data; cout << data.a << endl; return 0; }
大家觉得这段代码有问题嘛?
我们此时D类已经出现了数据冗余和二义性的问题了,我们直接访问 a 的值是不可行的,只有明确地表明你要访问地是哪个类里面的 a
class A { public: int a; }; class B : virtual public A { public: int b; }; class C : virtual public A { public: int c; }; class D : public B, public C { public: int d; }; int main() { D data; cout << data.a << endl; return 0; }
不过可以在腰部,也就是 B和C 类加上 virtual ,就可以数据二义性的问题和数据冗余,这个叫 菱形虚拟继承
菱形虚拟继承:
class A { public: int a; }; //class B : virtual public A class B : public A { public: int b; }; //class C : virtual public A class C : public A { public: int c; }; class D : public B, public C { public: int d; }; int main() { D data; //data.a = 1; data.B::a = 2; data.C::a = 6; data.b = 3; data.c = 4; data.d = 5; return 0; }
我们来看一下上述代码运行完,在内存的角度观察他们的变化:
能够看的出,总共分为三个部分:上面两个框分别是B和C类的,谁先继承谁就在上面,并且,数据a 也是独立开来的,B里面a = 2;C里面a = 6 。
再来看加了virtual后的又会是什么样的变化:
同样也是分为了三个部分,先继承的类就在上面,所以上面两个分别是B和C类,但这次不一样的是:
数据a并没有分开来,而是在最下面去了,相代替的是一个指针,这个指针指向的是一个虚基表,这个表里面存放着一个偏移量,当我们要进行 切割 的时候,获取 数据a 就通过当前的这个指针 + 这个偏离量就能找到了。(这个表叫虚基表,指针叫虚基表指针)
如果此时定义一个B对象,它的里面同时也有一个续集表指针,指向一个自己的续集表,这个偏移量 和 D定义出来的 对象 里的 B类的虚基表里的偏离量不同
如图所示:左边是B类定义出来的对象;右边是D类定义出来的对象。
他们都有各自的虚基表指针,这个指针是不一样的!!!!里面的偏移量也是!!