编译器构造 - C++中多个继承对象的内存布局

compiler construction - memory layout of a multiple-inherited object in C++

本文关键字:对象 继承 内存 布局 C++ 编译器      更新时间:2023-10-16
class B1 {
    virtual void f1();
    int int_in_b1;
};
class B2 {
    virtual void f2();
    int int_in_b2;
};
class D: B1, B2 {
    int int_in_d;
    void f1();
    void f2();
};
class D1: B1, B2 {
    int int_in_d;
    virtual void f1();
    virtual void f2();
};

根据本文,类D的对象d的内存布局如下所示:

d:
  +0: pointer to virtual method table of D (for B1)
  +4: value of int_in_b1
  +8: pointer to virtual method table of D (for B2)
 +12: value of int_in_b2
 +16: value of int_in_d
virtual method table of D (for B1):
  +0: D::f1()  // B1::f1() is overridden by D::f1()
virtual method table of D (for B2):
  +0: D::f2()   // B2::f2() is overridden by D::f2()

D1的对象呢?在类D1中,成员f1f2都被声明为virtual

virtual的使用在D1中是多余的。

从 C++11 §10.3¶2 开始:

如果一个虚成员函数vf是在类Base和类Derived中声明的,直接或间接派生自Base,则成员函数vf声明的名称、参数类型列表(8.3.5)、cv限定符和ref限定符(或缺少相同Base::vf),Derived::vf也是虚的(无论它是否如此声明),并且它覆盖了111 Base::vf


111)具有相同名称但不同参数列表(条款13)的函数作为虚函数不一定是虚函数,也不会覆盖。在重写函数的声明中使用 virtual 说明符是合法的,但冗余(具有空语义)。在确定覆盖时不考虑访问控制(第 11 条)。

因此,内存布局(这似乎是问题所在)对于DD1是相同的。显然,不同的类型将具有不同的虚拟表。

f1()f2()D中也是虚拟的。如果函数被声明为 virtual,则它在继承的任何类中都保持 virtual 状态。

因此,类 DD1 内部几乎没有区别(但显然是不同的类型),但它们都给出了 f1f2 的实现,它们的虚拟表也会有所不同,因为D的虚拟表将指向D的实现,D1也是如此。

vtable for D:
    f1: D::f1
    f2: D::f2
vtavle for D1:
    f1: D1::f1
    f2: D1::f2