8

私はこのプログラムでコンパイラーの間にいくつかの矛盾を発見しました、

struct A {
};

struct B : public A {
    float m;
};

struct C : public A {
    B b;
    float n;
};

struct D : public A {
    float n;
    B b;
};

static_assert(sizeof(A) == 1, "");
static_assert(sizeof(B) == 4, "");
static_assert(sizeof(C) == 8, ""); // most compilers say this is 12
static_assert(sizeof(D) == 8, "");

ほとんどのコンパイラは、sizeof(C)== 8で、sizeof(C)は実際には12であると主張します。私が見つけた唯一のコンパイラはそうではなく、8であると言っているのはMicrosoft VisualStudio2010です。

私より賢い人から言われた理由は、B内にAの2つの別個の参照があり、互いに異なる個々のオフセットを保持する必要があるためです。まず、Cから派生したAはオフセット0にあり、メンバーb内の2番目のAは0の最初のAと同じオフセットにすることはできないため、4バイトのパディングが挿入されます。

ほとんどのコンパイラはこの動作を実装しているので、両方のAが異なる参照を持っていることを確認するためにどのような場合が必要か疑問に思いました。なぜこれが当てはまるのかについての直感をお探しですか?

誰かがそれが規格によって要求される条件であるかもしれないと言いました、そして、我々はそれの理由が何であるかについて興味がありましたか?

ありがとうございました

4

3 に答える 3

3

はい、これは10p8で言及されています:

基本クラスのサブオブジェクトのサイズはゼロである可能性があります(第9節)。ただし、同じクラスタイプを持ち、同じ最も派生したオブジェクトに属する2つのサブオブジェクトは、同じアドレス(5.10)に割り当てられてはなりません

2CAのがあり、1つは継承され、もう1つはの一部ですB。Microsoftは、空の基本クラスの最適化を、すべきでないときに積極的かつ誤ってここで採用しています。既知のバグだと思いますが、MicrosoftConnectでバグレポートを見つけるのは本当に難しいです。

于 2012-12-05T23:24:27.353 に答える
3

この規格では、同じタイプの各オブジェクトのアドレスが異なることが明確に義務付けられています。関連する条項は5.10[expr.eq]段落1です。

同じタイプの2つのポインターは、両方がnullであるか、両方が同じ関数を指しているか、または両方が同じアドレス(3.9.2)を表している場合にのみ、等しく比較されます。

これは、2つのオブジェクトを区別するために必要です。オブジェクトには、価値とアイデンティティの両方があります。基本クラスのサブオブジェクトの場合、それを含むクラスと同じアドレスを持つのが妥当です。クラスのメンバーの場合、タイプによって2つのオブジェクトのIDを区別できます。つまり、同じアドレスを持っていても問題ありません。同じタイプの2つのオブジェクトの場合でも、オブジェクトのIDを区別するための何かが必要になります。

于 2012-12-05T23:31:21.237 に答える
1

C ++では、オブジェクトはデータのペア(アドレスとタイプ)によって一意に決定されます。

あなたが正しく観察したように、両方Cとには、これが真でなければならないD2つの異なるサブオブジェクトが含まれAています。ただし、メモリ内でのレイアウト方法によっては、8バイト構造内の異なるアドレスBに両方のサブオブジェクトを配置できない場合があることがわかります。A

サブオブジェクトの実際の数値アドレスを印刷してみませんか?

于 2012-12-05T23:32:26.547 に答える