構造体と共用体
クラスは参照型ですが、構造体は値型です。 Cの構造体はすべて、Dの構造体として正確に表現することができます。 C++ 用語で言うと、 Dの構造体は POD (Plain Old Data) 型、 すなわち"自明な"コンストラクタとデストラクタを持った型とも言えます。 構造体と共用体は、シンプルなデータの集約や、ハードウェアや外部の型の上に データ構造を定義する方法として使うことを意図されています。 外部の型とは、OSのAPIや、ファイルフォーマットなどによって定義される型です。 オブジェクト指向的な機能はクラスによって提供されます。
構造体には同一性(identity)の概念はありません。 つまり、 実装は必要ならいつでも構造体のビット単位のコピーを作って良いことになっています。
Feature | 構造体 | クラス | C の構造体 | C++ の構造体 | C++ のクラス |
---|---|---|---|---|---|
値型 | X | X | X | X | |
参照型 | X | ||||
データメンバ | X | X | X | X | X |
隠れメンバ | X | X | X | ||
静的メンバ | X | X | X | X | |
デフォルト初期化子 | X | X | |||
ビットフィールド | X | X | X | ||
非仮想メンバ関数 | X | X | X | X | |
仮想メンバ関数 | X | X | X | ||
コンストラクタ | X | X | X | ||
デストラクタ | X | X | X | ||
RAII | X | X | X | ||
代入のオーバーロード | X | X | |||
構造体リテラル | X | ||||
演算子Overload | X | X | X | X | |
継承 | X | X | X | ||
不変条件 | X | X | |||
単体テスト | X | X | |||
synchronized | X | ||||
テンプレート化 | X | X | X | X | |
アラインメント調整 | X | X | |||
アクセス保護 | X | X | X | X | |
デフォルト public | X | X | X | X | |
タグ名前空間 | X | X | X | ||
無名構造体/クラス | X | X | X | X | |
静的コンストラクタ | X | X | |||
静的デストラクタ | X | X |
AggregateDeclaration: struct Identifier StructBody union Identifier StructBody struct Identifier ; union Identifier ; StructTemplateDeclaration UnionTemplateDeclaration Tag: struct union StructBody: { } { StructBodyDeclarations } StructBodyDeclarations: StructBodyDeclaration StructBodyDeclaration StructBodyDeclarations StructBodyDeclaration: DeclDef StructAllocator StructDeallocator StructAllocator: ClassAllocator StructDeallocator: ClassDeallocator
Cの場合と同様に動きます。ただし、以下の例外を除きます:
- ビットフィールドはありません
- アラインメントを明示的に指定できます
- タグ名の名前空間は分離されていません - タグ名は現在のスコープに入ります
- 次のような宣言:
struct ABC x;
は不正です。次のように書きます:ABC x;
- 他の構造体/共用体のメンバとして、無名構造体/共用体が使えます
- メンバのデフォルト初期化子を指定できます
- メンバ関数やstaticメンバを作れます
構造体の静的初期化
静的構造体メンバは、 メンバに対して指定されたデフォルト初期化値へと初期化されます。 指定がなかった場合は、そのメンバの型のデフォルト初期化値が使用されます。 もし静的初期化子が指定されていれば、メンバは メンバ名 コロン 式、 の三つ組みで初期化されます。メンバ初期化の順序は問われません。 静的メンバの初期化子はコンパイル時に評価可能なものに限られます 初期化リストに指定されていないメンバは、 デフォルト値で初期化されます。struct X { int a; int b; int c; int d = 7;} static X x = { a:1, b:2}; // c は 0, d は 7 static X z = { c:4, b:5, a:2 , d:5}; // z.a = 2, z.b = 5, z.c = 4, z.d = 5C方式の、 メンバの順序に基づいた初期化もサポートされています:
static X q = { 1, 2 }; // q.a = 1, q.b = 2, q.c = 0, q.d = 7
static変数の初期化には構造体リテラルも使用可能ですが、 コンパイル時評価可能なものに限られます。
static X q = S( 1, 2+3 ); // q.a = 1, q.b = 5, q.c = 0, q.d = 7
static初期化子の構文は、メンバ名を指定しない形式に限り、 非static変数の初期化にも使用可能です。 この場合はコンパイル時評価の不可能な式を指定することもできます。
void test(int i) { S s = { 1, i }; // q.a = 1, q.b = i, q.c = 0, q.d = 7 }
共用体の静的初期化
共用体は明示的に初期化します。union U { int a; double b; } static U u = { b : 5.0 }; // u.b = 5.0初期化子と重なるけれどもより大きなメモリを使うメンバがある場合、 その部分は、 0初期化されます。
構造体の動的初期化
構造体は、 同じ型の別の値を使って動的に初期化できます:
struct S { int a; } S t; // デフォルト初期化 t.a = 3; S s = t; // s.a は 3 になる
opCall がその構造体でオーバーライドされていて、 別の型の値で構造体が初期化されようとしているときには、 opCall 演算子が呼び出されます:
struct S { int a; static S opCall(int v) { S s; s.a = v; return s; } static S opCall(S v) { S s; s.a = v.a + 1; return s; } } S s = 3; // s.a は 3 になる S t = s; // t.a は 3 にある。 S.opCall(s) は呼ばれない
構造体リテラル
構造体リテラルは、 構造体名のあとに括弧で引数リストを続けた物です:
struct S { int x; float y; } int foo(S s) { return s.x; } foo( S(1, 2) ); // フィールド x を 1 に、フィールド y を 2 にセット。
構造体リテラルは、構文的には関数呼び出しのように見えます。 構造体がメンバ関数 opCall を実装していた場合は、 その構造体のリテラルは使えません。 フィールド数より多い引数を書くと エラーです。 フィールド数より引数の数が少ない場合は、 残りのフィールドは デフォルト初期化されます。 構造体のメンバに無名unionが会った場合、 構造体リテラルで初期化できるのは1つめのunionのメンバのみです。 残りの、 重なっていない部分はデフォルト初期化されます。
構造体のプロパティ
.sizeof | 構造体のbyte単位でのサイズ |
.alignof | 構造体が整列されなければならないバイト境界の値 |
.tupleof | フィールドのタプルを取得 |
構造体フィールドのプロパティ
.offsetof | 構造体の開始位置からのオフセットバイト数 |