我想知道 C++ 标准是否保证单继承使对象向上“增长”,即给定 aclass Base
和 a class Derived: public Base
,以及一个指针Derived* ptr
,其结果在dynamic_cast<Base*>(ptr)
数值上总是小于或等于ptr
.
2 回答
不,内存布局是实现细节。
话虽这么说,假设没有虚函数,我不知道任何实际上没有这样做的实现。如果您在中引入虚函数Derived
(但在中没有),则可以(取决于实现)将Base
虚表指针放在字段之前(使大于)。Base
Base*
Derived*
澄清:
上面的示例是特定于 Visual C++ 的。您可以使用以下代码检查它:
class Base {
int X;
};
class Derived : public Base {
virtual void f() {
}
int Y;
};
int main() {
Derived d;
Derived* d_ptr = &d;
Base* b_ptr = dynamic_cast<Base*>(d_ptr); // static_cast would be enough BTW.
bool base_smaller_or_equal = (ptrdiff_t)b_ptr <= (ptrdiff_t)d_ptr;
return 0;
}
将base_smaller_or_equal
在false
Visual C++ 下。从@enobayram 的评论来看,它应该true
在 GCC 之下。
在任何情况下,它都是一个实现细节,不能依赖。
不。
但是,现在还没有必要恐慌。这类问题虽然在标准 C++ 中没有完全解决,但编译器将遵循的 ABI 文档回答了此类问题。许多编译器,例如 gcc、Clang 或 icc(但不是VC++)都遵循Itanium ABI。
在 Itanium ABI 中,只要您有单个非虚继承并且 Base 类有虚方法,那么 Derived 和 Base 将始终具有相同的地址。
话虽如此,这是您实际上不必担心的实现。您可以完美地编写符合 C++ 标准的代码,并且仍然可以管理您的用例。问题是 C++ 允许您将任何指针转换为void*
和char*
(后者作为非混叠的特定例外)并返回。您唯一需要担心的是,当您将 aBase*
转换为 a时,void*
您需要将其转换回 aBase*
而不是 a Derived*
。也就是说,你输入的类型和你返回的类型应该匹配。
然而,要(确定地)知道对象的大小要困难得多。这需要应用sizeof
到对象的当前动态类型并且没有工具来获取它virtually
。
我的建议是实际检测您的基类:
class Base {
public:
char const* address() const { return (char const*)dynamic_cast<void const*>(this); }
size_t offset() const { return this->address() - (char const*)this; }
virtual size_t size() const { return sizeof(Base); } // to be overriden
virtual ~Base() {}
};
这有助于获取您需要的所有信息(参见演示)。请注意,使用 Itanium ABIoffset()
将始终返回0
,但至少您没有在这里假设。