【问题标题】:sizeof derived class with virtual base and virtual functionsizeof 具有虚基和虚函数的派生类
【发布时间】:2013-08-25 17:02:03
【问题描述】:

我遇到了 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. BaseValue 和 DerivedValue--8 字节
  2. 指针表示基类的成员偏移量 (虚继承特性)--4字节
  3. 指针表示Base类的虚函数表--4字节
  4. 指针表示只属于Derived类的虚函数Func2()--4字节 (就我而言,没有非虚基类并获得其独特虚函数的派生类应该有自己的虚表)

总计 20 个字节;

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

【问题讨论】:

  • 是的,你弄错了。我很困惑,因为您暗示您知道虚函数表是什么,然后在下一句中表明您不知道虚函数表是如何工作的。你认为虚函数表是如何工作的?
  • 是的,我想我知道虚函数表是如何工作的,比如说,如果我得到一个 Base 的实例 b,我可以得到 Func() 的地址为 (int*)*(&b) ,
  • 这是实现定义的,你应该避免依赖这些信息。例如,一个 int 可能是 2 个字节(有人说是 68k 吗?),指针的大小也可能是其他大小。现在如果你问是因为你想了解 vtable,那是另一回事。
  • 你的意思是vtable机制是编译器相关的?还是指针的大小?我假设在 32 位环境中指针是 4 个字节
  • 不知道怎么用英文正确表达“very next sentence”,我的意思是一个指针,表示基成员(这里是BaseValue)在内存中的偏移量Derived 类的一个实例

标签: c++ function virtual-inheritance


【解决方案1】:

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

啊,我现在看到了问题。这不是相当虚函数表的工作方式。当Base 被定义时,编译器注意到它需要一个虚拟表,并为Base 生成一个虚拟表,带有一个指向Base::Func 实现的指针(Func)。定义Derived 时,编译器注意到它继承自Base,并为Base 生成一个函数表,其中有两个 指针,Func 指向Derived::Func,@987654330 @ 指向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 是“劫持”Bases 虚函数表,事实上,这正是虚函数发挥其魔力的方式。

(到处都是假设,这些都没有明确定义,虚拟继承使事情复杂化,等等等等)

【讨论】:

  • 好的,我明白我误解了什么,也许我应该检查一下。我认为只有在基类是非虚拟的情况下才会发生这种情况,以便将 Func2 添加到继承的 vtable 中,否则,如果它得到一个没有出现在其基类中的虚函数,它应该得到一个新的虚拟指针
  • Ghostblade,不,即使Derived 虚拟继承自Base,它仍然会覆盖Base 的虚拟指针。否则,虚函数会失败,不是吗?
猜你喜欢
  • 2015-10-24
  • 1970-01-01
  • 1970-01-01
  • 2020-10-26
  • 2011-11-16
  • 2020-09-30
  • 1970-01-01
  • 2016-08-13
  • 2012-11-02
相关资源
最近更新 更多