22.値クラス
他のクラス同様に、値クラスはフィールド、メンバ関数、そして、ネスト型を含むことができます。
値クラスは値型オブジェクトにアクセスするのに遠回しなメモリ要求することなく、効果的に、早く、データのコピーを可能とするよう設計されています。
その結果として、データ表現に値クラスを使うことはガベージ・コレクタの負担を減らし、そして、値クラスをリソース管理には不適切なものとしています。
全ての値型と同様に、値クラスはボックス化できます。(
§14.2.6 )
[注記:
§12.2.1 に書かれているように、C++/CLI に提供されている基本型、int や double そして、bool のような、
は値クラス型に対応しています。値クラスと演算子多重定義はこの仕様書において新しい"基本の"型を実装するために使うことができます。]
22.1 値クラス宣言
値クラスは
class-key value class もしくは、value struct によって定義されたクラスです。
value class 定義と value struct 定義はメンバのデフォルトアクセス可能性において異なります。
デフォルトでは、value class のメンバは private で、value struct のメンバは public です。
値クラス定義は
attributes(属性)(
§29 )、
top-level-visibility(上層型識別)(
§12.4 )、
class-modifiers(クラス修飾子)(
§19.1.1 )、そして、
base-clause(基底節)(
§22.1.1 )
のセットを含むことができます。
全ての値クラスは暗黙のうちに sealed (継承不可)です。
(そのため、この修飾子の明示的な使用は冗長です。)
値クラス定義はネイティブ・クラス定義中にネストすることが可能です。
しかしながら、値クラス定義の中にネイティブ・クラス定義をはネストするべきではありません。
メタデータの詳細は、
§34.7.1 を参照のこと。
22.1.1 値クラス基底仕様
値クラス定義は
base-clause(基底節) 指定を含むことができます。
それは、その値クラスが実装するインターフェイスのみを定義します。
全ての値クラス型はその基底クラスとして System::ValueType を持っています。
もし、
base-specifier(基底指定子)が
access-specifier(アクセス指定子)を含む場合、その
access-specifier(アクセス指定子)は public であるべきです。
もし、
base-specifier(基底指定子)が
access-specifier(アクセス指定子)を含まない場合、例え値クラスが value class キーワードによって定義されていたとしても、
access-specifier(アクセス指定子)は暗黙のうちに public です。
もし、値クラス定義が一つ以上の
base-specifier(基底指定子)を含んでいたら、値クラスはそれらのインターフェイスを実装するよう言われています。
(インターフェイスの実装については後の
§25.3 で議論します。)
22.2 値クラス・メンバ
値クラスのメンバはその
member-specification(メンバ指定子)によって導入されるメンバと System::ValueType から継承されるメンバ全てを含みます。
値クラスのメンバ関数は
cv-qualifier-seq を持つべきではありません。
値クラスはネイティブ配列やネイティブクラスを型とするメンバを含むべきではありません。
[注意:そのような型をメンバとして許すのは親の型が混合型(
§23 )となるでしょう。]
値クラスはメンバにビット・フィールドを含むべきではありません。
値クラスは friend 宣言するべきではありません。
値クラスはどんなアクセス宣言子も含むべきではありません。
値クラスはデフォルト・コンストラクタ、コピー・コンストラクタ、代入演算子を含むべきではありません。
全ての値クラスはコピー可能です。
§22.3 で記述された違いを除いて、
§21.11 と
§21.13 を通して
§21.2 で提供されたクラス・メンバに関する記述は、
同様に値クラス・メンバにも当てはまります。
[注意:値クラスのメンバ関数は hidebysig 名前検索(
§10.7 )を使います。]
値クラスのメンバ関数はローカル・クラスを含むべきではありません。
いくつかの値クラス・メンバ宣言、メンバ・アクセス、そしてメンバ関数呼び出しはメタデータ生成の間に特殊な扱いを要求します。
より詳しい情報は
§34.9 を参照のこと。
22.3 refクラスと値クラスの違い
22.3.1 継承
全ての値クラス型は暗黙のうちに System::ValueType から継承しています。
そして、同様にそれは、System::Object クラスから継承しています。
値クラス宣言は実装するインターフェイスのリストを指定することができますが、基底クラスを指定することはできません。
値クラスは sealed (継承不可)です。
[注意:継承は値クラス型ではサポートされていないのですが、 protected, protected private、ないし、protected public のアクセス指定子をメンバが持つことは許されています。
しかしながら、品質のよい実装はそのような場合は警告を出すかもしれません。]
22.3.2 デフォルト値
値クラスのデフォルト値はデフォルト・コンストラクタによって返された値に対応します。
refクラスと違い、値型構造体はパラメータのないインスタンス・コンストラクタを宣言することを許されていません。
その代わり、全ての値クラスは暗黙のうちにパラメータのないインスタンス・コンストラクタを持っており、それは全ての値型フィールドにそのデフォルト値を、
そして、全てのハンドル型フィールドに nullptr を設定した結果を値として返します。
[注意:値クラスはデフォルトの初期化済み状態が正規な状態と考えられるよう設計されるべきです。以下のコードにおいて、
value class KeyValuePair {
String^ key;
String^ value;
public:
KeyValuePair(String^ key, String^ value) {
if ( key == nullptr || value == nullptr )
throw gcnew ArgumentException();
this->key = key;
this->value = value;
}
};
ユーザーが定義したインスタンス・コンストラクタはそれが明示的に呼び出された場合にのみヌル値に対して保護されます。
KeyValuePair 変数がデフォルト値で初期化された場合、key と value のフィールドは null となり、そして、値クラスはこの状態を扱うよう用意されているべきでしょう。]
22.3.3 this の意味
ref クラス T のインスタンス・コンストラクタかインスタンス・メンバ関数中で、this は型 T^ の右辺値として扱います。
値クラス V のインスタンス・コンストラクタかインスタンス・メンバ関数中で、this は interior_ptr<T> 型の右辺値として扱います。
[注意:ネイティブ・クラスと違い、this それ自身は const-qualified ではありません。]
22.3.4 デストラクタとファイナライザ
デストラクタかファイナライザを持つ値クラスは不正です。
[注意:値クラスは決してリソースを管理しません。そのため、値クラス中のデストラクタとファイナライザはリソースを回収する必要がありません。
値型はリソースを表現できますが、そのような場合、そのような値型を持つクラスはファイナライザとデストラクタを持つべきです。
例えば、ファイル記述子を値クラスは表現できます。メンバとしてファイル記述子を使う場合、適切なAPIを使ってファイルを閉じる責任があります。]
メタデータは
§34.7.13.2 を参照のこと。
22.4 単純値クラス
simple value class(単純値クラス)はガベージ・コレクタによって追跡される必要があるようなメンバを持たない値クラスです。
単純値クラスは次に含まれるもののみです。
- インスタンス・フィールドを持たない値クラス
- インスタンス・フィールドの全てが次の型のどれかである値クラス:基本型、列挙型、ポインタ、または他の単純値クラス
単純値クラスは new 演算子で生成される、ネイティブ・クラスは単純値クラス型のメンバを持つことができます。
22.5 コンストラクタ
デフォルト・コンストラクタやコピー・コンストラクタを持つ値クラスは不正です。
値クラスのデフォルト生成機構は全てのメンバが 0 にされたバイトで表現されます。
値クラスのコピー生成機構は常に値型の全てのメンバがビット単位でコピーされます。
それ以外には、値クラスはインスタンス・コンストラクタ(
§19.9 )と静的コンストラクタ(
§19.10 )を持つことができます。
22.6 演算子
コピー代入演算子を持つ値クラスは不正です。値クラスのコピー機構は常にその値クラスの全てのメンバがビット単位でコピーされます。