指针是否始终指向基<;=指向派生类的指针? 我想知道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(){}
};

这有助于获取所需的所有信息()。请注意,使用安腾ABI
offset()时
将始终返回
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]
来过度简化