具有虚拟基和虚拟函数的派生类的大小
sizeof derived class with virtual base and virtual function
我在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
的结果,即:
- 基本值和派生值--8字节
- 指针表示基类的成员偏移量(虚拟继承的特性)-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::Func
,Func2
指向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
的虚拟函数表,事实上,这正是虚拟函数发挥魔力的方式。
(假设无处不在,没有一个是定义明确的,虚拟本质使事情复杂化,等等)
- 用常见虚拟函数实现的任意组合来实现派生类的正确方法是什么
- 有没有比在库中添加一个并非由所有派生类实现的新虚拟函数更好的设计实践
- 大小虚拟继承中的派生类
- 是否可以使用基类非虚拟方法中的派生类虚拟方法?
- 派生类调用父类的方法,该方法调用重写的虚拟方法调用错误的方法
- 虚拟基类函数中派生类的大小
- 从纯虚拟类 (A) 派生的指针无法访问来自纯类 (B) 的重载方法
- 使(虚拟)函数在大多数派生类中无法访问中间基类中可访问,定义良好?
- 派生类中纯虚拟基方法的专业化
- 基类可以声明虚拟方法但不定义它吗?仍然在派生类中定义
- 无法在派生对象上运行虚拟函数
- googletest:测试基类具有纯虚拟方法的派生类时的核心转储
- 使用回调函数从构造函数调用虚拟/派生方法的替代方法?
- 有没有办法在没有虚拟的情况下使用基类指针调用派生类函数
- 从基调用派生类的隐藏(非虚拟)方法
- 派生类可以有多个指向虚拟表的指针
- 如何从派生类函数中调用虚拟函数
- (为什么)纯虚拟派生类中是否需要虚拟基类构造函数调用?
- 虚拟派生和转换模棱两可
- 删除虚拟派生类的this和位置new