4

データのパック方法が標準で指定されていないことは知っています。私はクラスのメモリレイアウトについてのアイデアを得ようとしていました(特にdynamic_cast<void*>、最も派生したクラスの先頭へのポインターを返すことを保証する方法)。次のコードの出力についての説明は思いつきませんでした:

struct A{ int a;};
struct B{ int b;};
struct C: public A, public B { int c;};
struct D:public C {int d;};


int main(){
  D* ob=new D;
  A* a = ob;
  B* b = ob;
  C* c = ob;
}

ポインターの値を出力すると、acdが常に同じ値を持ち、bオフセットとして 4 バイトが追加されるだけであることがわかります。偶然ですか?それともその背後にロジックがありますか?

編集: 概念的にはレイアウトは画像のようにする必要がありますが、どういうわけかポイントA、C、Dが1つにマージされます。ここに画像の説明を入力

4

3 に答える 3

8

まず、あなたstruct A

| int a |

そしてB_

| int b |

struct Cとを継承struct Astruct B、メンバーも 1 つありint cます。したがって、次のようなレイアウトにすることができます。

            struct B
struct A     /
   \        /
| int a | int b | int c |

およびstruct Dを継承するstruct Cは、

            struct B
struct A     /
   \        /
| int a | int b | int c | int d |
\-----------------------/
         struct C

について考えてみましょうD* ob = new D;。次のようになります。

| int a | int b | int c | int d |
^
\
 ob

そして考えてみてくださいA* a = ob-struct Aは のオフセット 0 にstruct Dあるので、

| int a | int b | int c | int d |
^
\
 a

に等しいstruct cです。

ただし、 になるとstruct B、オフセット 4 (の場合sizeof(int) == 4) にあるので、-

| int a | int b | int c | int d |
        ^
        /
       b

レイアウトは標準では定義されておらず、実装ごとに異なる可能性があることに注意してください。可能なレイアウトの1つを示しました。

高度な情報については、C++ Multiple Inheritance Memory Layout with "Empty classes"を読むことをお勧めします。

于 2014-08-05T11:43:36.507 に答える
0

あなたの推論は正しいです。ただし、レイアウトは標準で定義されていないため、信頼することはできません。そうは言っても、ほとんどのコンパイラは、図に描いたレイアウトを選択します。

于 2014-08-05T11:43:20.927 に答える
0

ポインターの値を出力すると、a、c、d は常に同じ値を持ち、b のみがオフセットとして 4 バイト追加されることがわかります。

D オブジェクトでは、C サブオブジェクトが最初に来るので、それが完全な D オブジェクトと同じアドレスを持っていることは驚くべきことではありません (C オブジェクトの前に何を期待しているのですか? なぜ余分なバイトがDの始まり?)

C オブジェクトでは、A サブオブジェクトが最初に来るため、C オブジェクトと同じアドレスを持つことは驚くべきことではありません。したがって、C が D のサブオブジェクトである場合、それも同じアドレスを持つことになります。完全な D オブジェクトとして。

偶然ですか?それともその背後にロジックがありますか?

偶然ではありません。コンパイラの ABI によって定義されます。多くのコンパイラは、クラスをどのように配置する必要があるかを文書化したItanium C++ ABIに従っています。

于 2014-08-05T13:20:05.720 に答える