C++菱形继承

Edit


C++支持多继承,当父类继承自同一个超类,则即为本篇所述的菱形继承。类图如下:

主要参考两个链接:

前一篇提到的VS2003与后文中的实现方式相同,前一篇中还提到了Bjarne方案,不知哪些编译器采用这种方案。所以本文认为前者方案为主流方案,加以阐述。后者方案可视为前者方案的略微改良版。

单类虚继承

代码实现如下:

struct A {
virtual foo();
int X;
}
struct B : virtual public A {
virtual foo();
virtual fooB();
int Y;
}

其中VPTR指向Virtual Table (VTBL),VBPTR指向Virtual Base Table (VBTBL)。其内容为:

  1. 第一项为与本类VPTR的偏移。一般VPTR总是存在对象开始处。所以上例为-4字节,取补即为0xFFFFFFFC。如果本类没有虚函数,就没有VPTR,则该值为0。
  2. 从第二项开始为到各基类的偏移值,有几个基类,表项就有几个。上例中,只有一个基类,且本类成员变量会放在VBPTR之后,所以到基类A的偏移量即为8字节。

注意1: 只有出现了虚拟继承,才会有这样的内存布局,如果没有虚拟继承,则父类VPTR及其成员会挨着子类VPTR放置。
注意2: 此时VPTR中内容和无虚继承时的内容也不同。若B是普通继承A,则B类内存布局为:

菱形继承

对于这样一个菱形继承:

struct A
{
A(int v=100):X(v){};
virtual void foo(void){};
int X;
};
struct B :virtual public A
{
B(int v=10):Y(v),A(100){};
virtual void foo(void){};
virtual void fooB(void){}
int Y;
};
struct C : virtual public A
{
C(int v=20):Z(v),A(100){};
virtual void foo(void){};
virtual void fooC(void){};
int Z;
};
struct D : public B, public C
{
D(int v =40):B(10),C(20),A(100),L(v){};
virtual void foo(void){};
virtual void fooD(void){};
int L;
};

其主要特点为:

  • ABCD均包含虚函数,所以每个类都有虚函数表
  • 每个类均有成员变量
  • 父类BC对祖类采用虚继承
  • 子类对两个父类采用普通继承

这应当是最复杂的情况了,当有些类没有虚函数,或者没有成员变量时,情况可能稍许简单些。

A

A类因为没有虚拟继承,所以其内存布局为普通布局:

B / C

BC类均为单类虚继承,如上节所述:

D

D类最复杂

  1. 本类无覆盖关系的成员函数在最一开始的VPTR中,或认为在B::VPTR中
  2. 本类成员函数在所有直接父类对象的后面
  3. 只有一份A对象
  4. 本类如果覆盖任何父类或祖先类的成员函数会覆盖对应对象的VPTR中的项目。

Error: source type is not polymorphic

当祖先类A没有虚函数的时候,在发生向下转型(子类指针转成父类指针)的时候会出问题。可以查看Memory Layout for Multiple and Virtual Inheritance - Edsko de Vries, January 2006 中Downcasting一节。
考虑这样两个多继承的类:

Bottom* bottom1 = new Bottom();
AnotherBottom* bottom2 = new AnotherBottom();
Top* top1 = bottom1;
Top* top2 = bottom2;
Left* left = static_cast<Left*>(top1);

Bottom和AnotherBottom都继承自Left,Left虚继承自Top。现在要把一个Top指针转换成一个Left指针。使用static_cast会报error: cannot convert from base `Top’ to derived type `Left’ via virtual base `Top’。 因为需要运行时信息,因为所有的VPTR和对象内存布局,都是到运行时才建立的。于是想到可以用dynamic_cast

Left* left = dynamic_cast<Left*>(top1);

此时会报另一个错误:error: cannot dynamic_cast `top’ (of type `class Top*’) to type `class Left*’ (source type is not polymorphic)。这是因为Top类没有虚函数,所以此时Bottom和AnotherBottom对象的Top部分没有VPTR。于是编译器也分不清这是一个int还是一个对象。解决的方法就是为Top加一个虚函数。最方便的方法就是加一个空的虚析构函数,如下:

class Top {
virtual ~Top(){};
}

总结

当发生虚继承的时候,对象内部内存布局会产生大的变化,在引入VBPTR的同时,其各基类对象的排布顺序也发生了变化。

其他参考链接

%23%20C++%u83F1%u5F62%u7EE7%u627F%0A@%28myblog%29%5Bc/c++%5D%0AC++%u652F%u6301%u591A%u7EE7%u627F%uFF0C%u5F53%u7236%u7C7B%u7EE7%u627F%u81EA%u540C%u4E00%u4E2A%u8D85%u7C7B%uFF0C%u5219%u5373%u4E3A%u672C%u7BC7%u6240%u8FF0%u7684%u83F1%u5F62%u7EE7%u627F%u3002%u7C7B%u56FE%u5982%u4E0B%uFF1A%0A%21%5BAlt%20text%20%7C300x0%5D%28./1520316988125.png%29%0A%0A%u4E3B%u8981%u53C2%u8003%u4E24%u4E2A%u94FE%u63A5%uFF1A%0A-%20%5Bc++%u865A%u7EE7%u627F%u5BF9%u8C61%u7684%u5185%u5B58%u5E03%u5C40%5D%28http%3A//blog.csdn.net/BlueDog/article/details/4711169%29%0A-%20%5BMemory%20Layout%20for%20Multiple%20and%20Virtual%20Inheritance%20-%20Edsko%20de%20Vries%2C%20January%202006%5D%28https%3A//cs.nyu.edu/courses/fall16/CSCI-UA.0470-001/slides/MemoryLayoutMultipleInheritance.pdf%29%0A%0A%u524D%u4E00%u7BC7%u63D0%u5230%u7684VS2003%u4E0E%u540E%u6587%u4E2D%u7684%u5B9E%u73B0%u65B9%u5F0F%u76F8%u540C%uFF0C%u524D%u4E00%u7BC7%u4E2D%u8FD8%u63D0%u5230%u4E86Bjarne%u65B9%u6848%uFF0C%u4E0D%u77E5%u54EA%u4E9B%u7F16%u8BD1%u5668%u91C7%u7528%u8FD9%u79CD%u65B9%u6848%u3002%u6240%u4EE5%u672C%u6587%u8BA4%u4E3A%u524D%u8005%u65B9%u6848%u4E3A%u4E3B%u6D41%u65B9%u6848%uFF0C%u52A0%u4EE5%u9610%u8FF0%u3002%u540E%u8005%u65B9%u6848%u53EF%u89C6%u4E3A%u524D%u8005%u65B9%u6848%u7684%u7565%u5FAE%u6539%u826F%u7248%u3002%0A%0A%23%23%20%u5355%u7C7B%u865A%u7EE7%u627F%0A%u4EE3%u7801%u5B9E%u73B0%u5982%u4E0B%uFF1A%0A%60%60%60%0Astruct%20A%20%7B%0A%09virtual%20foo%28%29%3B%0A%09int%20X%3B%0A%7D%0Astruct%20B%20%3A%20virtual%20public%20A%20%7B%0A%09virtual%20foo%28%29%3B%0A%09virtual%20fooB%28%29%3B%0A%09int%20Y%3B%0A%7D%0A%60%60%60%0A%21%5BAlt%20text%5D%28./1520393159326.png%29%0A%0A%u5176%u4E2DVPTR%u6307%u5411Virtual%20Table%20%28VTBL%29%uFF0CVBPTR%u6307%u5411Virtual%20Base%20Table%20%28VBTBL%29%u3002%u5176%u5185%u5BB9%u4E3A%uFF1A%0A1.%20%u7B2C%u4E00%u9879%u4E3A%u4E0E%u672C%u7C7BVPTR%u7684%u504F%u79FB%u3002%u4E00%u822CVPTR%u603B%u662F%u5B58%u5728%u5BF9%u8C61%u5F00%u59CB%u5904%u3002%u6240%u4EE5%u4E0A%u4F8B%u4E3A-4%u5B57%u8282%uFF0C%u53D6%u8865%u5373%u4E3A0xFFFFFFFC%u3002%u5982%u679C%u672C%u7C7B%u6CA1%u6709%u865A%u51FD%u6570%uFF0C%u5C31%u6CA1%u6709VPTR%uFF0C%u5219%u8BE5%u503C%u4E3A0%u3002%0A2.%20%u4ECE%u7B2C%u4E8C%u9879%u5F00%u59CB%u4E3A%u5230%u5404%u57FA%u7C7B%u7684%u504F%u79FB%u503C%uFF0C%u6709%u51E0%u4E2A%u57FA%u7C7B%uFF0C%u8868%u9879%u5C31%u6709%u51E0%u4E2A%u3002%u4E0A%u4F8B%u4E2D%uFF0C%u53EA%u6709%u4E00%u4E2A%u57FA%u7C7B%uFF0C%u4E14%u672C%u7C7B%u6210%u5458%u53D8%u91CF%u4F1A%u653E%u5728VBPTR%u4E4B%u540E%uFF0C%u6240%u4EE5%u5230%u57FA%u7C7BA%u7684%u504F%u79FB%u91CF%u5373%u4E3A8%u5B57%u8282%u3002%0A%0A**%u6CE8%u610F1**%3A%20%u53EA%u6709%u51FA%u73B0%u4E86%u865A%u62DF%u7EE7%u627F%uFF0C%u624D%u4F1A%u6709%u8FD9%u6837%u7684%u5185%u5B58%u5E03%u5C40%uFF0C%u5982%u679C%u6CA1%u6709%u865A%u62DF%u7EE7%u627F%uFF0C%u5219%u7236%u7C7BVPTR%u53CA%u5176%u6210%u5458%u4F1A%u6328%u7740%u5B50%u7C7BVPTR%u653E%u7F6E%u3002%0A**%u6CE8%u610F2**%3A%20%u6B64%u65F6VPTR%u4E2D%u5185%u5BB9%u548C%u65E0%u865A%u7EE7%u627F%u65F6%u7684%u5185%u5BB9%u4E5F%u4E0D%u540C%u3002%u82E5B%u662F%u666E%u901A%u7EE7%u627FA%uFF0C%u5219B%u7C7B%u5185%u5B58%u5E03%u5C40%u4E3A%uFF1A%0A%21%5BAlt%20text%5D%28./1520393183452.png%29%0A%0A%23%23%20%u83F1%u5F62%u7EE7%u627F%0A%u5BF9%u4E8E%u8FD9%u6837%u4E00%u4E2A%u83F1%u5F62%u7EE7%u627F%uFF1A%0A%21%5BAlt%20text%5D%28./1520376926158.png%29%0A%u4EE3%u7801%u5B9E%u73B0%u5982%u4E0B%uFF1A%0A%60%60%60%0Astruct%20A%0A%7B%0A%20%20%20%20A%28int%20v%3D100%29%3AX%28v%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20foo%28void%29%7B%7D%3B%0A%20%20%20%20int%20X%3B%0A%7D%3B%0Astruct%20B%20%3Avirtual%20public%20A%0A%7B%0A%20%20%20%20B%28int%20v%3D10%29%3AY%28v%29%2CA%28100%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20foo%28void%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20fooB%28void%29%7B%7D%0A%20%20%20%20int%20Y%3B%0A%7D%3B%0Astruct%20C%20%3A%20virtual%20public%20A%0A%7B%0A%20%20%20%20C%28int%20v%3D20%29%3AZ%28v%29%2CA%28100%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20foo%28void%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20fooC%28void%29%7B%7D%3B%0A%20%20%20%20int%20Z%3B%0A%7D%3B%0Astruct%20D%20%3A%20public%20B%2C%20public%20C%0A%7B%0A%20%20%20%20D%28int%20v%20%3D40%29%3AB%2810%29%2CC%2820%29%2CA%28100%29%2CL%28v%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20foo%28void%29%7B%7D%3B%0A%20%20%20%20virtual%20void%20fooD%28void%29%7B%7D%3B%0A%20%20%20%20int%20L%3B%0A%7D%3B%0A%60%60%60%0A%u5176%u4E3B%u8981%u7279%u70B9%u4E3A%uFF1A%0A-%20ABCD%u5747%u5305%u542B%u865A%u51FD%u6570%uFF0C%u6240%u4EE5%u6BCF%u4E2A%u7C7B%u90FD%u6709%u865A%u51FD%u6570%u8868%0A-%20%u6BCF%u4E2A%u7C7B%u5747%u6709%u6210%u5458%u53D8%u91CF%0A-%20%u7236%u7C7BBC%u5BF9%u7956%u7C7B%u91C7%u7528%u865A%u7EE7%u627F%0A-%20%u5B50%u7C7B%u5BF9%u4E24%u4E2A%u7236%u7C7B%u91C7%u7528%u666E%u901A%u7EE7%u627F%0A%0A%u8FD9%u5E94%u5F53%u662F%u6700%u590D%u6742%u7684%u60C5%u51B5%u4E86%uFF0C%u5F53%u6709%u4E9B%u7C7B%u6CA1%u6709%u865A%u51FD%u6570%uFF0C%u6216%u8005%u6CA1%u6709%u6210%u5458%u53D8%u91CF%u65F6%uFF0C%u60C5%u51B5%u53EF%u80FD%u7A0D%u8BB8%u7B80%u5355%u4E9B%u3002%0A%23%23%23%20A%0AA%u7C7B%u56E0%u4E3A%u6CA1%u6709%u865A%u62DF%u7EE7%u627F%uFF0C%u6240%u4EE5%u5176%u5185%u5B58%u5E03%u5C40%u4E3A%u666E%u901A%u5E03%u5C40%uFF1A%0A%21%5BAlt%20text%5D%28./1520393228683.png%29%0A%0A%23%23%23%20B%20/%20C%0ABC%u7C7B%u5747%u4E3A%u5355%u7C7B%u865A%u7EE7%u627F%uFF0C%u5982%u4E0A%u8282%u6240%u8FF0%uFF1A%0A%21%5BAlt%20text%5D%28./1520393159326.png%29%0A%0A%23%23%23%20D%0AD%u7C7B%u6700%u590D%u6742%0A%21%5BAlt%20text%5D%28./1520393848842.png%29%0A%u9700%u8981%u6CE8%u610F%u7684%u662F%uFF1A%0A1.%20%u672C%u7C7B%u65E0%u8986%u76D6%u5173%u7CFB%u7684%u6210%u5458%u51FD%u6570%u5728%u6700%u4E00%u5F00%u59CB%u7684VPTR%u4E2D%uFF0C%u6216%u8BA4%u4E3A%u5728B%3A%3AVPTR%u4E2D%0A2.%20%u672C%u7C7B%u6210%u5458%u51FD%u6570%u5728%u6240%u6709%u76F4%u63A5%u7236%u7C7B%u5BF9%u8C61%u7684%u540E%u9762%0A3.%20%u53EA%u6709%u4E00%u4EFDA%u5BF9%u8C61%0A4.%20%u672C%u7C7B%u5982%u679C%u8986%u76D6%u4EFB%u4F55%u7236%u7C7B%u6216%u7956%u5148%u7C7B%u7684%u6210%u5458%u51FD%u6570%u4F1A%u8986%u76D6%u5BF9%u5E94%u5BF9%u8C61%u7684VPTR%u4E2D%u7684%u9879%u76EE%u3002%0A%0A%23%23%23%20Error%3A%20source%20type%20is%20not%20polymorphic%0A%u5F53%u7956%u5148%u7C7BA%u6CA1%u6709%u865A%u51FD%u6570%u7684%u65F6%u5019%uFF0C%u5728%u53D1%u751F%u5411%u4E0B%u8F6C%u578B%uFF08%u5B50%u7C7B%u6307%u9488%u8F6C%u6210%u7236%u7C7B%u6307%u9488%uFF09%u7684%u65F6%u5019%u4F1A%u51FA%u95EE%u9898%u3002%u53EF%u4EE5%u67E5%u770B%5BMemory%20Layout%20for%20Multiple%20and%20Virtual%20Inheritance%20-%20Edsko%20de%20Vries%2C%20January%202006%5D%28https%3A//cs.nyu.edu/courses/fall16/CSCI-UA.0470-001/slides/MemoryLayoutMultipleInheritance.pdf%29%20%u4E2DDowncasting%u4E00%u8282%u3002%0A%u8003%u8651%u8FD9%u6837%u4E24%u4E2A%u591A%u7EE7%u627F%u7684%u7C7B%uFF1A%0A%21%5BAlt%20text%5D%28./1520396776572.png%29%0A%60%60%60cpp%0ABottom*%20bottom1%20%3D%20new%20Bottom%28%29%3B%0AAnotherBottom*%20bottom2%20%3D%20new%20AnotherBottom%28%29%3B%0ATop*%20top1%20%3D%20bottom1%3B%0ATop*%20top2%20%3D%20bottom2%3B%0ALeft*%20left%20%3D%20static_cast%3CLeft*%3E%28top1%29%3B%0A%60%60%60%0ABottom%u548CAnotherBottom%u90FD%u7EE7%u627F%u81EALeft%uFF0CLeft%u865A%u7EE7%u627F%u81EATop%u3002%u73B0%u5728%u8981%u628A%u4E00%u4E2ATop%u6307%u9488%u8F6C%u6362%u6210%u4E00%u4E2ALeft%u6307%u9488%u3002%u4F7F%u7528%60static_cast%60%u4F1A%u62A5error%3A%20cannot%20convert%20from%20base%20%5C%60Top%27%20to%20derived%20type%20%5C%60Left%27%20via%20virtual%20base%20%5C%60Top%27%u3002%20%u56E0%u4E3A%u9700%u8981%u8FD0%u884C%u65F6%u4FE1%u606F%uFF0C%u56E0%u4E3A%u6240%u6709%u7684VPTR%u548C%u5BF9%u8C61%u5185%u5B58%u5E03%u5C40%uFF0C%u90FD%u662F%u5230%u8FD0%u884C%u65F6%u624D%u5EFA%u7ACB%u7684%u3002%u4E8E%u662F%u60F3%u5230%u53EF%u4EE5%u7528%60dynamic_cast%60%u3002%0A%60%60%60%0ALeft*%20left%20%3D%20dynamic_cast%3CLeft*%3E%28top1%29%3B%0A%60%60%60%0A%u6B64%u65F6%u4F1A%u62A5%u53E6%u4E00%u4E2A%u9519%u8BEF%uFF1Aerror%3A%20cannot%20dynamic_cast%20%5C%60top%27%20%28of%20type%20%5C%60class%20Top%5C*%27%29%20to%20type%20%5C%60class%20Left%5C*%27%20%28source%20type%20is%20not%20polymorphic%29%u3002%u8FD9%u662F%u56E0%u4E3ATop%u7C7B%u6CA1%u6709%u865A%u51FD%u6570%uFF0C%u6240%u4EE5%u6B64%u65F6Bottom%u548CAnotherBottom%u5BF9%u8C61%u7684Top%u90E8%u5206%u6CA1%u6709VPTR%u3002%u4E8E%u662F%u7F16%u8BD1%u5668%u4E5F%u5206%u4E0D%u6E05%u8FD9%u662F%u4E00%u4E2Aint%u8FD8%u662F%u4E00%u4E2A%u5BF9%u8C61%u3002%u89E3%u51B3%u7684%u65B9%u6CD5%u5C31%u662F%u4E3ATop%u52A0%u4E00%u4E2A%u865A%u51FD%u6570%u3002%u6700%u65B9%u4FBF%u7684%u65B9%u6CD5%u5C31%u662F%u52A0%u4E00%u4E2A%u7A7A%u7684%u865A%u6790%u6784%u51FD%u6570%uFF0C%u5982%u4E0B%uFF1A%0A%60%60%60%0Aclass%20Top%20%7B%0A%20virtual%20%7ETop%28%29%7B%7D%3B%0A%7D%0A%60%60%60%0A%0A%23%23%20%u603B%u7ED3%0A%u5F53%u53D1%u751F%u865A%u7EE7%u627F%u7684%u65F6%u5019%uFF0C%u5BF9%u8C61%u5185%u90E8%u5185%u5B58%u5E03%u5C40%u4F1A%u4EA7%u751F%u5927%u7684%u53D8%u5316%uFF0C%u5728%u5F15%u5165VBPTR%u7684%u540C%u65F6%uFF0C%u5176%u5404%u57FA%u7C7B%u5BF9%u8C61%u7684%u6392%u5E03%u987A%u5E8F%u4E5F%u53D1%u751F%u4E86%u53D8%u5316%u3002%0A%0A%23%23%20%u5176%u4ED6%u53C2%u8003%u94FE%u63A5%0A-%20%5B%u8BE6%u8C08C++%u865A%u51FD%u6570%u8868%u90A3%u56DE%u4E8B%uFF08%u591A%u91CD%u7EE7%u627F%u5173%u7CFB%2C%20%u65E0%u865A%u7EE7%u627F%uFF09%5D%28http%3A//blog.csdn.net/Li_Ning_/article/details/51893748%29%0A-%20%5B%u3010C++%u3011c++%u5355%u7EE7%u627F%u3001%u591A%u7EE7%u627F%u3001%u83F1%u5F62%u7EE7%u627F%u5185%u5B58%u5E03%u5C40%uFF08%u865A%u51FD%u6570%u8868%u7ED3%u6784%uFF09%5D%28http%3A//lib.csdn.net/article/cplusplus/51023%29%20%uFF08%u6B64%u7BC7%u5305%u542B%u4E86%u5BF9%u300Ac++%u865A%u7EE7%u627F%u5BF9%u8C61%u7684%u5185%u5B58%u5E03%u5C40%u300B%u4E00%u6587%u7684%u9610%u91CA%0A