Ahm*_*AEK 1 c++ polymorphism class vtable object-layout
我有一个关于sizeof使用 CRTP 实现接口的类实例的问题,如下例所示。
#include <iostream>
class Interface
{
public:
virtual int foo() {
std::cout << "Interface\n";
return 0;
};
};
template <class Derived>
class Base : Interface
{
public:
virtual int foo() override {
Interface::foo();
std::cout << "base\n";
return static_cast<Derived*>(this)->bar2;
}
int bar;
};
class Derived : public Base<Derived>
{
public:
virtual int foo() override {
std::cout << "Derived\n";
Base<Derived>::foo();
return bar;
}
int bar2;
};
int main()
{
std::cout << sizeof(Base<Derived>) << '\n';
std::cout << sizeof(Derived) << '\n';
return 0;
}
Run Code Online (Sandbox Code Playgroud)
两者gcc和clang输出16为 和sizeof(Derived),sizeof(Base<Derived>)如果我删除 Base 并在 Derived 中实现它,则结果相同,就好像它们都是相同的对象godbolt 示例
问题是,为什么会发生这种情况?当编译器看到使用 CRTP 时,它是否会简单地将Base<Derived>和组合到一个类中?Derived或者我错过了什么?编译器如何摆脱在 Derived 中存储指向 vtable 的额外指针并将其大小缩小到只有 1 个指针大小的开销?这种方法是否有任何隐藏的开销?
我期望编译器将一个指向 vtbl 的指针,Derived然后是 int,然后是一个指向 vtblBase的指针,然后是一个 int,然后是一个指向接口的 vtbl 的指针,以Derived将其大小转换为40字节,但情况似乎并非如此。
如果打印出数据成员bar和的偏移量bar2,就会很明显发生了什么:
std::cout << std::bit_cast<std::ptrdiff_t>(&Derived::bar) << '\n';
std::cout << std::bit_cast<std::ptrdiff_t>(&Derived::bar2) << '\n';
Run Code Online (Sandbox Code Playgroud)
这打印:
8
12
Run Code Online (Sandbox Code Playgroud)
由此看来,类的布局如下所示:
8
12
Run Code Online (Sandbox Code Playgroud)
注:vptr是指向该对象的vtable的指针。
请记住,每个多态基类只有一个 vtable 指针。当派生类初始化时,它替换vptr基类中的子对象。另请参阅:vtable 存储在内存中的哪个位置?
的最后四个字节Base<Derived>是填充,并将Derived其bar2成员放入该填充中。这是允许的,因为它Base<Derived>是一个多态类,因此不是标准布局。因此,即使Derived多了一个数据成员,Base<Derived>它也不会占用更多的空间。
如果添加另一个int bar3成员,它应该占用 24 个字节。
| 归档时间: |
|
| 查看次数: |
107 次 |
| 最近记录: |