2013/11/04(月)C++98 C++03 の POD
独自コンテナ template
メンバ追加時にコンストラクタもデストラクタも呼ばなくてよい。
コンテナを clear するときも、for (int i = 0; i < size(); i++) this[i]->~T(); する必要が無い。
では、型をPODとして定義するにはどうすればいいのかという話だ。
C++11の情報ばかりヒットして、
C++03までのPODについて、いまいちまとまった情報が得られないので、
自分なりに調べた情報をまとめておく。
C++03までのPOD
PODはaggregateな構造体もしくは共用体。
aggregate
aggregate は 配列、もしくは 次の要件を満たす class である。
- ユーザ定義のコンストラクタを持たない
- 基底クラスを持たない
- static でない private/protected な データメンバ を持たない
- virtual なメンバ関数を持たない
aggregate ならば、初期化時に SomeClass a = ...; といったように中括弧初期化が行える。
メンバの数が足りなければ、足りない部分は0で埋まる。(0とは0だったり0.0だったりfalseだったりする)
中括弧内の明示的な記述数は0個でもよい(←Cでは違った気がする)。
POD
POD構造体は次の要件を満たすaggregateなstruct。
- aggregate である。
- ユーザ定義のコンストラクタを持たない
- 基底クラスを持たない
- static でない private/protected な データメンバ を持たない
- virtual なメンバ関数を持たない
- ユーザ定義のデストラクタを持たない。
- ユーザ定義の operator = を持たない。
- staticでない non-POD型の構造体(とその配列)をメンバを持たない
- staticでない non-POD型の共用体(とその配列)をメンバに持たない
- staticでない 参照 を持たない。
POD構造体は次の要件を満たすaggregateなunion。
- aggregate である。aggregateとはつまり...
- ユーザ定義のコンストラクタを持たない
- 基底クラスを持たない
- static でない private/protected な データメンバ を持たない
- virtual なメンバ関数を持たない
- ユーザ定義のデストラクタを持たない。
- ユーザ定義の operator = を持たない。
- staticでない non-POD型の構造体(とその配列)を持たない。
- staticでない non-POD型の共用体(とその配列)を持たない。
- staticでない 参照を持たない。
補足
C++では、オブジェクトがPODであるときのみ、メモリ上にフィールドが宣言した順番に並ぶことが保証される。
つまり、逆に言えば、コンストラクタ・デストラクタを宣言した時点で、
そのオブジェクトのデータメンバのメモリ上の順番は保証されなくなる。
virtual でないメンバ関数ならば持てる。
operator =以外のoperatorオーバーロードも持てる。
POD型のオブジェクトは、明示的にコンストラクタを呼べば、
データメンバはゼロ初期化される(これはC++03から/Visual Studioなら2005以降)
組み込み型はPODである。
検証コード
ideoneでの実行結果は不定なものゼロになってますが、これはたまたま。
上記コードをVS2013のC++でデバッグビルドすると、
-858993460 0 -842150451 0 -842150451 0 -858993460 0 -858993460 0
が得られます。ここで、
値 -858993460 は 0xCCCCCCCC で stack上の未初期化領域を表し、
値 -842150451 は 0xCDCDCDCD で heap上の未初期化領域を表しています。
C++11では
C++11以降のPODについては色々と制約がゆるくなってるので、また時間があるときにでも書く。
C++11では、コンストラクタを自分で定義しつつ、デフォルトコンストラクタを残すようなことができるので、
各種デフォルトでコンパイラが生成する関数群が(暗黙的・明示的問わず)残されてればよい的な方向にシフトしてる。