浅谈虚继承
一、提出问题。
有以下代码。
class B { public: int a; }; class D1 : public B { public: int b; }; class D2 : public B { public: int c; }; class E : public D1, public D2 { public: int d; };
其类图为,
B |
int a 4 |
D1 |
B::a , int c 8 |
D2 |
B::a , int d 8 |
E |
D1::B::a , D1::int b , D2::B::a , D2:: int c , int e 20 |
通过分析内存形态我们发现E中基类B被重复继承了,显然我们不想要这种结果。继续验证一下。
用基类的指针指向子类的对象。
D1 * pd1 = new D1(); B * pb = pd1;
基类指针指向了D1的对象,也就是:D1的对象,是一种B。这样是可以的。
E pe = new E(); D1 * pd1 = pe; //ok D2* pd2 = pe; //ok B* pb = pe; //error 从E到B转换不明确
二、引入虚继承
以上代码中,我们使用虚继承的方式。
class D1 : virtual public B
class D2 : virtual public B
使用类图表示
对于程序员来说,这种类层次图显得更加简单和清晰,不过对于一个编译器来说,这就复杂得多了。
B |
int a |
4 |
D1 |
B::a , int c , vtbl B |
12 |
D2 |
B::a , int d , vtbl B |
12 |
E |
B::a , D1::int b , D2:: int c , int d, vtbl D1, vtbl D2 |
24 |
从这里我们看出,子类E每继承一个虚继承的类,都会多使用一个虚表来标记,也就会多占4个字节。
此时我们再验证一下
E* pe = new E(); B* pb = pe; ///ok
虚继承一般会用在菱形的继承结构里面。