10

我想知道 C++ 标准是否保证继承使对象向上“增长”,即给定 aclass Base和 a class Derived: public Base,以及一个指针Derived* ptr,其结果在dynamic_cast<Base*>(ptr)数值上总是小于或等于ptr.

4

2 回答 2

9

不,内存布局是实现细节。

话虽这么说,假设没有虚函数,我不知道任何实际上没有这样做的实现。如果您在中引入虚函数Derived(但在中没有),则可以(取决于实现)将Base虚表指针放在字段之前(使大于)。BaseBase*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_equalfalseVisual C++ 下。从@enobayram 的评论来看,它应该true在 GCC 之下。

在任何情况下,它都是一个实现细节,不能依赖。

于 2012-04-08T11:09:41.927 回答
7

不。

但是,现在还没有必要恐慌。这类问题虽然在标准 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,但至少您没有在这里假设

于 2012-04-08T11:55:27.883 回答