Tar*_*ras 5 c++ memory inheritance memory-management
这是一段代码:
struct Base
{
virtual void Foo() = 0;
virtual ~Base() { }
};
struct Derived : Base
{
virtual void Foo() override { }
unsigned long long some_new_data_members[42];
};
int test_offset()
{
Derived object{};
Base* base_subobject = &object;
return reinterpret_cast<unsigned char*>(base_subobject) - reinterpret_cast<unsigned char*>(&object);
}
Run Code Online (Sandbox Code Playgroud)
在test_offset我检查过的所有版本的 gcc、clang 和 msvc 编译器上,该函数总是返回 0。
我的问题是:C++ 标准是否保证在和之间存在单继承的情况下此函数将始终返回 0 ?如果没有,有人可以提供一个现实生活中的例子(也许有一些异国情调的平台),其中这段代码将返回不同于 0 的东西?BaseDerived
请不要回答多重继承!
更新:
正如用户VTT所指出的,这样的代码可能会导致非零偏移:
struct Base
{
};
struct Derived : Base
{
virtual void Foo() { }
};
Run Code Online (Sandbox Code Playgroud)
它产生的偏移量等于 msvc 上的指针大小。
但是这个例子与我的代码片段的根本区别在于Derived这里的类引入了一些新的虚函数。因此,一个新的 vtable 指针必须出现在 中的某处Derived,它可能被放置在Base子对象之前,给我们一个非零偏移量。
所以,现在我想更具体地询问我的场景。如果Base类已经有一些虚函数,并且Derived只是覆盖它们而不添加任何新函数(尽管它可能会添加一些新的非静态成员字段)怎么办?在这种特殊情况下,这种非零偏移量是否可能?
指向不同对象的指针具有相同值的属性称为指针可相互转换。该标准列出了保证这一点的所有可能情况:
6.7.2化合物类型[basic.compound]
4个两个对象a和b是指针的相互转换的,如果:
(4.1)它们是相同的对象,或者
(4.2)是一个联合对象,而另一个是一个非静态数据成员object (12.3) 或
(4.3) 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员,或者,如果该对象没有非静态数据成员,则为第一个基类子对象在那个对象 (12.2) 或
(4.4) 中,存在一个对象c,使得a和c是指针可相互转换的,并且c和b是指针可相互转换的。
如果两个对象是指针可相互转换的,则它们具有相同的地址,并且可以通过areinterpret_cast(8.5.1.10)从指向另一个的指针获得指向一个的指针。[注意:数组对象和它的第一个元素不是指针可相互转换的,即使它们具有相同的地址。——尾注]
例子:
struct Base{};
struct Derived : Base
{
virtual void Foo() { }
};
Run Code Online (Sandbox Code Playgroud)
Derived对于基类子对象前面添加的额外 vtable 指针,偏移量将为 8(在典型的 64 位平台上)。