10

C++ 標準によって、単一継承によってオブジェクトが上向きに「成長」することが保証されclass Baseているのだろうか。class Derived: public BaseDerived* ptrdynamic_cast<Base*>(ptr)ptr

4

2 に答える 2

9

いいえ。メモリレイアウトは実装の詳細です。

そうは言っても、仮想関数がないと仮定して、実際にそれを行わない実装を私は知りません。に仮想関数を導入した場合Derived(ただし、に仮想関数はありませんでしたBase)、仮想テーブルポインタ(実装に応じて)Baseフィールドの前に配置できます(Base*より大きい値になりますDerived*)。

明確化:

上記の例は、VisualC++固有のものです。次のコードを使用してチェックアウトできます。

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;

}

VisualC++の下にbase_smaller_or_equalあります。false@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*andにキャストしてchar*(後者は非エイリアシングの特定の例外として)、その逆にキャストできることです。心配する必要がある唯一のことは、 aBase*を a にケース化するとき、 aではなく avoid*にキャストする必要があるということです。つまり、入力した型と返される型が一致する必要があります。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() {}
};

これは、必要なすべての情報を取得するのに役立ちます (デモを参照)。Itanium ABIではoffset()常に が返されることに注意してください。0ただし、少なくともここでは想定していません。

于 2012-04-08T11:55:27.883 に答える