我想知道C ++标准是否保证单个继承使对象“向上”增长,给出一个class Base
和一个class Derived: public Base
,以及一个指针Derived* ptr
dynamic_cast<Base*>(ptr)
的结果将始终在数字上小于或等于ptr
。
答案 0 :(得分:9)
没有。内存布局是实现细节。
话虽如此,假设没有虚函数,我不知道任何实际上没有这样做的实现。如果在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;
}
{C}下的base_smaller_or_equal
将为false
。根据@ enobayram的评论,它应该是GCC下的true
。
无论如何,这是一个实施细节,不能依赖。
答案 1 :(得分:7)
没有
然而,没有必要恐慌。虽然标准C ++中没有完全解决这类问题,但编译器将遵循ABI文档。许多编译器,如gcc,Clang或icc(但不是 VC ++)都遵循Itanium ABI。
在Itanium ABI中,只要您具有单个非虚拟继承并且Base类具有虚方法,则Derived和Base将始终具有相同的地址。
话虽这么说,这是一个你实际上不用担心的实现。您可以完美地编写符合C ++标准的代码,并仍然可以管理您的用例。问题是C ++允许您将任何指针转换为void*
和char*
(后者作为非别名的特定例外)并返回。您唯一需要担心的是,当您将Base*
加到void*
时,您需要将其转回Base*
而不是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() {}
};
这有助于获取所需的所有信息(see demo)。请注意,使用Itanium ABI offset()
将始终返回0
,但至少您不是假设。