是指向base的指针< =指向派生类的指针?

时间:2012-04-08 11:03:55

标签: c++ inheritance casting

我想知道C ++标准是否保证单个继承使对象“向上”增长,给出一个class Base和一个class Derived: public Base,以及一个指针Derived* ptr dynamic_cast<Base*>(ptr)的结果将始终在数字上小于或等于ptr

2 个答案:

答案 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,但至少您不是假设