具有虚拟基和虚拟函数的派生类的大小

sizeof derived class with virtual base and virtual function

本文关键字:虚拟 派生 函数      更新时间:2023-10-16

我在C++虚拟继承方面遇到问题。

我有一个如下的类层次结构:

class Base
{
public:
    virtual void Func(){};
    int BaseValue;
};
class Derived : virtual public Base
{
public:
    void Func(){};
    virtual void Func2(){};
    int DerivedValue;
};

然而,它编译得很好,我对内存结构有点困惑。

我希望得到sizeof(Derived)==20的结果,即:

  1. 基本值和派生值--8字节
  2. 指针表示基类的成员偏移量(虚拟继承的特性)-4字节
  3. 指针表示基类的虚拟函数表--4字节
  4. 指针表示只属于派生类的虚拟函数Func2()——4字节(就我而言,没有非虚拟基类并获得其唯一虚拟函数的派生类应该有自己的虚拟表)

总计20个字节;

然而Xcode 4.6产生了sizeof(Derived)==16的不同结果,我错了吗?

指针表示基类的虚拟函数表--4字节
指针表示虚拟函数Func2(),它只属于class派生的——4个字节(就我而言,派生类没有非虚拟基类,并获得其唯一的虚拟函数应该有自己的虚拟表)

啊,我现在明白问题了。这并不是虚拟函数表的工作方式。定义Base时,编译器会注意到它需要一个虚拟表,并为Base生成一个具有一个指针(Func)的虚拟表,该指针指向Base::Func实现。定义Derived时,编译器会注意到它继承自Base,并为Base生成一个函数表,该表具有两个指针,Func指向Derived::FuncFunc2指向Derived::Func2

然后,如果创建了Base的实例,那么您提到的函数表指针指向Base表,对Func的任何调用都将重定向到Base::Func

如果创建了Derived的实例,则其内部Base对象的虚拟函数表指针将改为指向Derived表。Base只知道如何访问Func指针,但Func指针现在指向Derived::Func,所以这就是get的调用。它没有意识到它指向的是另一张桌子。在代码中,它可能看起来更像这样:

using voidFunctionType = void(*)();
struct BaseVTable {
    voidFunctionType Func;
}BaseVTableGlobal; 
struct Base {
    Base() :vTable(&BaseVTableGlobal) {}
    void Func() {vTable->Func();}
    BaseVTable* vTable; //4 bytes
    int BaseValue; //4 bytes
}; //total is 8 bytes
struct DerivedVTable : public BaseVTable  {
    voidFunctionType Func;
    voidFunctionType Func2;
}DerivedVTableGlobal; 
//inherits 8 bytes, +4 for virtual inheritance = 12
struct Derived : virtual public Base { 
    Derived() :Base() {vTable = &DerivedVTableGlobal;} //the shared vTable points at DerivedVTableGlobal
    void Func() {vTable->Func();} //base knows about Func, so this is easy
    void Func2() {((DerivedVTable*)vTable)->Func2();} //base doesn't know about Func2
    int DerivedValue; //4 bytes
}; //16 bytes total

所以XCode是对的。Derived"劫持"了Base的虚拟函数表,事实上,这正是虚拟函数发挥魔力的方式。

(假设无处不在,没有一个是定义明确的,虚拟本质使事情复杂化,等等)