クラスの不定なメモリレイアウト

C++を書きはじめて半年もすると、ほとんどのプログラマはクラスの定義を見ただけで生成されるオブジェクトのメモリレイアウトが浮かんでくるようになります。

構造体AAAのメモリレイアウト

構造体BBBのメモリレイアウト

このように一目です。(ただし浮かんでくるだけで合っているとは言っていない)

 

基本的にクラス内のメンバ変数は定義された順にメモリ上に並びます。

単一継承の場合、親クラス→子クラスというメモリレイアウトになります。

 

もう少し複雑な設計になるとどうでしょうか。

多少複雑になりました。

構造体DDDは仮想関数を持ち、多重継承しています。

これは64bit環境における一例です。

しかし、元のコードだけでは圧倒的に情報が不足しているため、この構造体定義から導き出されるメモリレイアウトは無数にあります。ある意味、どんなレイアウトを想像しても正解と言えなくもありません。

intは4byteかもしれませんし、8byteかもしれません。

vtableのポインタは多くの場合メンバの最初に置かれますがこれはコンパイラ依存です。さらに、そもそも動的なディスパッチを仮想関数テーブルによって実装しない場合はvtableはありません。

同様に多重継承した場合のレイアウトも宣言順とは限らず不定です。

 

クラスのメモリレイアウトに関して少しマイナーな情報もあります。

最もシンプルな例、構造体AAAに戻って話を進めましょう。

このメンバa〜e のメモリレイアウトは不定です。宣言順にabcdeと順序よく配置される保証はありません。

C++の規格ではあるアクセス指定子の範囲において宣言された順にレイアウトされることは保証していますがアクセスレベルを超えた宣言についてのメモリレイアウトには規程がありません。

Nonstatic data members of a (non-union) class with the same access control (Clause 11) are allocated so that later members have higher addresses within a class object. The order of allocation of non-static data members with different access control is unspecified (11).

__n3337

この場合d,eが連続したメモリに配置されることのみ保証されます。

aもbもprivateメンバですが、アクセス指定子を超えた場所で宣言されているのでレイアウトの保証対象外です。

もしあなたが複数のプラットフォーム向けにビルドする予定のアプリケーションを書く可能性があるのなら、どのように書くとメモリレイアウトが「不定」になるかを知ることは非常に重要です。

まとめ

  1. メンバ変数はあるアクセス指定子の中では宣言された順にレイアウトされる
  2. アクセス指定子をまたぐ場合のレイアウトは不定
  3. 仮想関数を持ったクラスのレイアウトも実装依存なので不定
  4. 多重継承のレイアウトも実装依存なので不定
  5. メモリレイアウトが不定になるシチュエーションを知ろう
Pocket