8

多重継承のメモリレイアウトが定義されていないことを知っているので、それに依存するべきではありません。ただし、特別な場合にそれを信頼できますか。つまり、クラスには「実際の」スーパークラスが1つだけあります。他のすべては「空のクラス」です。つまり、フィールドも仮想メソッドも持たないクラスです(つまり、非仮想メソッドしかありません)。この場合、これらの追加クラスは、クラスのメモリレイアウトに何も追加しないでください。(より簡潔に言えば、C ++ 11の表現では、クラスには標準レイアウトがあります)

すべてのスーパークラスにオフセットがないと推測できますか?例えば:

#include <iostream>

class X{

    int a;
    int b;
};

class I{};

class J{};

class Y : public I, public X,  public J{};

int main(){

    Y* y = new Y();
    X* x = y;
    I* i = y;
    J* j = y;

    std::cout << sizeof(Y) << std::endl 
                  << y << std::endl 
                  << x << std::endl 
                  << i << std::endl 
                  << j << std::endl;
}

YこれがX、唯一の実際の基本クラスであるクラスです。プログラムの出力(g ++ 4.6を使用してLinuxでコンパイルした場合)は次のとおりです。

8

0x233f010

0x233f010

0x233f010

0x233f010

結論として、ポインタの調整はありません。しかし、この実装は特定のものですか、それとも信頼できますか。つまり、タイプのオブジェクトを受け取った場合I(そして、これらのクラスのみが存在することがわかっている場合)、を使用してオブジェクトreinterpret_castをキャストできXますか?

オブジェクトのサイズは少なくとも1バイトでなければならないと仕様に記載されているので、私はそれを信頼できることを望んでいます。したがって、コンパイラは別のレイアウトを選択できません。IのメンバーのJ後ろにレイアウトする場合X、サイズはゼロになります(メンバーがないため)。したがって、唯一の合理的な選択は、オフセットなしですべてのスーパークラスを整列させることです。

Iここからreinterpret_castを使用した場合、私は正しいですか、それとも火遊びをしてXいますか?

4

1 に答える 1

11

C ++ 11では、コンパイラは標準のレイアウトタイプに空の基本クラス最適化を使用する必要があります。https://stackoverflow.com/a/10789707/981959を参照してください

特定の例では、すべてのタイプが標準レイアウトクラスであり、共通の基本クラスまたはメンバーがないため(以下を参照) 、C ++ 11でのその動作に依存できます (実際には、多くのコンパイラがすでにそのルールに従っていると思います) 、確かにG ++はそうしました、そして他のものはItanium C ++ ABIに続きます。)

警告:同じタイプの基本クラスがないことを確認してください。これらは別個のアドレスにある必要があるためです。

struct I {};

struct J : I {};
struct K : I { };

struct X { int i; };

struct Y : J, K, X { };

#include <iostream>

Y y;

int main()
{
  std::cout << &y << ' ' << &y.i << ' ' << (X*)&y << ' ' << (I*)(J*)&y << ' ' << (I*)(K*)&y << '\n';

}

プリント:

0x600d60 0x600d60 0x600d60 0x600d60 0x600d61

タイプYの場合、ベースの1つだけIがオフセットゼロになります。したがって、Xサブオブジェクトはオフセットゼロにあり(つまりoffsetof(Y, i)ゼロ)、ベースの1つIは同じアドレスにありますが、もう1つのIベースは(少なくともG ++では)です。およびClang++)オブジェクトに1バイト入ります。したがって、どのサブオブジェクトを指しているのかわからないために取得I*できなかっreinterpret_castた場合は、オフセット0またはオフセット1になります。X* III

非静的データメンバーがないため、コンパイラが2番目のIサブオブジェクトをオフセット1(つまり、の内部int)に配置しても問題ありません。そのため、そのアドレスの何かを実際に逆参照したりアクセスしたりすることはできず、オブジェクトへのポインタを取得するだけです。Iそのアドレスで。非静的データメンバーをに追加した場合IY標準レイアウトではなくなり、EBOを使用する必要がoffsetof(Y, i)なくなり、ゼロではなくなります。

于 2012-06-15T11:02:31.960 に答える