指针是否始终指向基<;=指向派生类的指针? 我想知道C++的标准是否保证了一个继承使对象“增长”,即给出了一个类基础< /C>和一个类:公共基础< /C> >和指针派生*PTR < /C> >,结果动态映射(PTR)在数值上总是小于或等于ptr
否。内存布局是实现细节 也就是说,假设没有虚拟函数,我不知道有任何实现实际上不这样做。如果在指针是否始终指向基<;=指向派生类的指针? 我想知道C++的标准是否保证了一个继承使对象“增长”,即给出了一个类基础< /C>和一个类:公共基础< /C> >和指针派生*PTR < /C> >,结果动态映射(PTR)在数值上总是小于或等于ptr,c++,inheritance,casting,C++,Inheritance,Casting,否。内存布局是实现细节 也就是说,假设没有虚拟函数,我不知道有任何实现实际上不这样做。如果在Derived中引入虚拟函数(但在Base中没有),虚拟表指针(取决于实现)可以放在Base字段之前(使Base*大于Derived*) 澄清: 下面的例子是Visual C++特定的。您可以使用以下代码将其签出: class Base { int X; }; class Derived : public Base { virtual void f() { } int Y
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;
}
类基{
int X;
};
派生类:公共基{
虚空f(){
}
int-Y;
};
int main(){
导出d;
派生*d_ptr=&d;
Base*b_ptr=dynamic_cast(d_ptr);//顺便说一句,静态_cast就足够了。
bool base_较小或等于=(ptrdiff_t)b_ptrNo
但是,没有必要在标准C++中完全解决这个问题,它是由编译器所遵循的ABI文档来回答的。许多编译器如GCC、CLAN或ICC(但不是VC++)遵循.</P>
在安腾ABI中,只要您有一个非虚拟继承,并且基类有一个虚拟方法,那么派生类和基类将始终具有相同的地址
这是一个你不需要担心的实现。你可以完美地编写C++标准兼容的代码,仍然管理你的用例。C++允许你把任何指针都投到<代码> VoUT*<代码>和<代码> char */COD>(后者是一个非混叠的特殊例外)。你唯一需要担心的是,当你把一个
Base*
转换成void*
时,你需要把它转换成Base*
而不是派生的*
。也就是说,你输入的类型和你得到的类型应该匹配
然而,要知道(确定)一个对象的大小要困难得多。这需要对对象的当前动态类型应用sizeof
,而实际上没有工具获取它
我的建议是实际为基类添加工具,以便:
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() {}
};
类基{
公众:
char const*address()const{return(char const*)dynamic_cast(this);}
size\u t offset()常量{返回this->address()-(char const*)this;}
要重写的虚拟大小\u t size()常量{return sizeof(Base);}//
虚拟~Base(){}
};
这有助于获取所需的所有信息()。请注意,使用安腾ABIoffset()时
将始终返回0
,但至少您没有在这里进行假设。我只记得指定了相同访问级别的成员对象地址的相对顺序。我不确定成员地址和对象本身的地址之间是否有任何保证关系……出于好奇,您为什么要问f好奇?我正在推送一些对象(它们之间涉及继承)作为Lua堆栈的用户数据,因此我必须处理类型擦除。我想知道,当从堆栈中检索用户数据时,我是否可以假设它等于指向Base
类的指针。注意:即使没有指定它,也要确保不涉及虚拟继承。事情可能会发生在这种情况下,我们会感到尴尬。GCC的情况并非如此。GCC将vtable
指针放在派生类布局的末尾,引入virtual
(或者可能正好放在基类的空格之后)。派生对象的地址在单个继承中不会更改。也就是说,这可能是一个不应该依赖的实现细节。@enobayram:不,在这种情况下,v指针将是第一个字段。这实际上不仅是gcc的要求,也是安腾ABI的要求。您可以很容易地演示它。好奇的是,我有JUt试图用GCC 4.6.1打印地址,你说得对。在我的32位系统上,我观察到&派生+4==(Base*)&派生
。我想知道上次尝试时有什么不同。这是一个问题。假设你有一个大小良好的未初始化存储(例如字符数据[N]
)如果您在其上构造新的派生的类,然后需要类型擦除,并且只知道基类,那么您可以假定此存储器中第一个字节的地址(&data[0]
)等于Base*
?给出了这里的答案,我认为你不能假设这一点,这令人担忧。你基本上需要存储一个包含指针差的额外整数,才能将数据
(如上)转换成Base*
,例如重新解释转换(数据+不同)
。遗憾的是。@gustafr:这是一个完全不同的问题。即使在没有继承的情况下,也不能保证您可以在数据中存储结构X
,并且由于对齐,您的X*
与数据一致。确实,您需要N>=sizeof(X)+alignof(X)-1
。在C++11中std::aligned_storage::type
起到了解救作用……但是您仍然存在一个问题:即使您使用std::aligned_storage::type
您也不能保证派生的与基本的具有相同的对齐方式……使用静态断言
或将差异存储在uint8\t中de>。事实上,aligned\u存储
是我真正的使用案例,为了简洁起见,我使用了一个char[N]
来过度简化