アラインメントまたはパディングビットについて言及せずに、この回答を完全な回答と見なす方法がわかりません。
アライメントの背景を少し説明しましょう。
「メモリ アドレス a は、a が n バイトの倍数 (n は 2 のべき乗) である場合、n バイト アラインされていると言われます。このコンテキストでは、バイトはメモリ アクセスの最小単位です。つまり、各メモリ アドレスは指定します。 n バイトにアラインされたアドレスは、2 進数で表現すると、log2(n) 個の最下位ゼロを持ちます。
b-bit 整列という別の言い回しは、ab/8 バイト整列アドレスを示します (例: 64 ビット整列は 8 バイト整列です)。
アクセスされるデータの長さが n バイトであり、データム アドレスが n バイトでアラインされている場合、メモリ アクセスはアラインされていると言われます。メモリー・アクセスがアライメントされていない場合、ミスアライメントと呼ばれます。定義により、バイト メモリ アクセスは常に整列されることに注意してください。
長さが n バイトのプリミティブ データを参照するメモリ ポインターは、n バイト アラインされたアドレスのみを含むことが許可されている場合、アラインされていると言われます。それ以外の場合は、アラインされていないと言われます。データ集合 (データ構造または配列) を参照するメモリ ポインターは、集合内の各プリミティブ データが整列されている場合にのみ整列されます。
上記の定義は、各プリミティブ データが 2 バイトの累乗長であると想定していることに注意してください。これが当てはまらない場合 (x86 の 80 ビット浮動小数点の場合のように)、コンテキストは、データが位置合わせされていると見なされるかどうかの条件に影響します。
データ構造は、制限付きと呼ばれる静的サイズのスタックまたは無制限と呼ばれる動的サイズのヒープのメモリに格納できます。" - Wiki から...
アラインメントを維持するために、コンパイラは構造体/クラス オブジェクトのコンパイル済みコードにパディング ビットを挿入します。" コンパイラ (またはインタプリタ) は通常、個々のデータ項目をアラインされた境界に割り当てますが、データ構造には異なるアラインメント要件を持つメンバーが含まれることがよくあります。適切なアラインメントを維持するために、トランスレータは通常、追加の名前のないデータ メンバーを挿入して、各メンバーが適切にアラインされるようにします。さらに、全体としてのデータ構造は、最終的な名前のないメンバーでパディングされる可能性があります. これにより、構造体の配列の各メンバーを適切に整列させることができます. .... ....
パディングは、構造体メンバーの後に、より大きなアライメント要件を持つメンバーが続く場合、または構造体の最後にのみ挿入されます" - Wiki
GCC がどのようにそれを行うかについての詳細は、以下を参照してください。
http://www.delorie.com/gnu/docs/gcc/gccint_111.html
テキスト「basic-align」を検索します
さて、この問題に行きましょう:
サンプル クラスを使用して、64 ビット Ubuntu で実行される GCC コンパイラ用にこのプログラムを作成しました。
int main() {
cout << "!!!Hello World!!!" << endl; // prints !!!Hello World!!!
A objA;
C objC;
cout<<__alignof__(objA.a)<<endl;
cout<<sizeof(void*)<<endl;
cout<<sizeof(int)<<endl;
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(C)<<endl;
cout<<__alignof__(objC.a)<<endl;
cout<<__alignof__(A)<<endl;
cout<<__alignof__(C)<<endl;
return 0;
}
そして、このプログラムの結果は次のとおりです。
4
8
4
16
16
32
4
8
8
では、説明しましょう。A と B の両方に仮想関数があるため、別々の VTABLE が作成され、それぞれのオブジェクトの先頭に VPTR が追加されます。
したがって、クラス A のオブジェクトには VPTR (A の VTABLE を指す) と int があります。ポインターの長さは 8 バイト、int の長さは 4 バイトになります。したがって、コンパイル前のサイズは 12 バイトです。ただし、コンパイラは int a の末尾に追加の 4 バイトをパディング ビットとして追加します。したがって、コンパイル後、A のオブジェクト サイズは 12+4 = 16 になります。
クラス B のオブジェクトについても同様です。
C のオブジェクトには 2 つの VPTR (クラス A とクラス B ごとに 1 つ) と 3 つの int (a、b、c) があります。したがって、サイズは 8 (VPTR A) + 4 (int a) + 4 (パディング バイト) + 8 (VPTR B) + 4 (int b) + 4 (int c) = 32 バイトになるはずです。したがって、C の合計サイズは 32 バイトになります。