クラス
Dのオブジェクト指向機能はすべてクラスから提供される。クラス階層 の ルートはObjectクラスである。Objectクラスは、 派生クラスが最低限必要とする機能と、その機能のデフォルト実装を定義する。
クラスはプログラマが定義する型である。クラスをサポートしていることが、 Dをオブジェクト指向言語としている。カプセル化、継承、 多態性を提供している。Dクラスは単一継承パラダイムをサポートしており、 インターフェースのサポートを追加することで拡張されている。クラスオブジェクトは参照によってのみインスタンス化される。
クラスをエクスポートすることができ、つまり、そのクラス名とすべての 非プライベート メンバーがDLLまたはEXEに外部公開される。
クラス宣言が定義される。
ClassDeclaration: class Identifier ; class Identifier BaseClassListopt AggregateBody ClassTemplateDeclaration BaseClassList: : SuperClassOrInterface : SuperClassOrInterface , Interfaces SuperClassOrInterface: BasicType Interfaces: Interface Interface , Interfaces Interface: BasicType
クラスは以下で構成される:
- スーパークラス
- インターフェース
- ダイナミックフィールド
- スタティックフィールド
- ネストされたクラス
- メンバ関数 "
- staticメンバ関数
- 仮想関数
- コンストラクタ
- デストラクタ
- クラスの不変条件
- 演算子オーバーロード
- その他の宣言(DeclDefを参照 )
クラスが定義される。
class Foo
{
... members ...
}
class Foo { } var;
代わりに、以下のようにする。class Foo { }
Foo var;
xml-ph-0000@deepl.internal アクセスコントロール
クラスメンバーへのアクセスは、 可視性属性を使用して制御される。 デフォルトの可視性属性はpublic である。
スーパークラス
すべてのクラスはスーパークラスを継承する。指定されていない場合は、 クラスは Object。Object は Dクラスの継承階層のルートを形成する。
class A { } // AはObjectを継承する class B : A { } // BはAを継承する
複数のクラス継承はサポートされていないが、クラスは複数のインターフェースから継承できる。 スーパークラスを宣言する場合は、インターフェースの前に置かなければならない。 継承する型を区切るにはカンマを使用する。
フィールド
クラスメンバーは常に. 演算子でアクセスされる。
ベースクラスのメンバーは、ベースクラスの名前の前に ドットを付けたものを付けることでアクセスできる。
class A { int a; int a2;} class B : A { int a; } void foo(B b) { b.a = 3; // フィールドB.aにアクセスする b.a2 = 4; // フィールドA.a2にアクセスする b.A.a = 5; // フィールドA.aにアクセスする }
Dコンパイラは、クラス内のフィールドの順序を自由に並べ替えることができ、 最適な形で実装定義の方法でパックすることができる。 フィールドは、関数内のローカル変数とよく似ていると考える ことができる。コンパイラは、いくつかのフィールドをレジスタに割り当て、他のフィールドをシャッフルして、 最適な スタックフレームのレイアウトを構築する。これにより、コード設計者は、 機械の最適化ルールに従ってコードを整理するのではなく、コードをより読みやすく整理する。 フィールドレイアウトの明示的な制御は、 クラスではなく、"構造体"や"共用体"の型によって提供される。
extern(Objective-C) クラスのフィールドは動的オフセットを持つ。つまり、 サブクラスがリコンパイルやリリンクを行う必要なく、ベースクラスが変更(インスタンス変数の追加や削除)できる ことを意味する。
クラスのプロパティ
Property | Description |
---|---|
.classinfo | クラスの動的型に関する情報。 |
.outer | ネストされたクラスインスタンスの場合、親クラスインスタンス、 または親クラスがない場合は親関数のコンテキストポインタの いずれかが提供される。 |
.tupleof | 下記を参照。 |
.tupleof
.tupleof プロパティは、 クラス内のすべての非静的フィールドの l値シーケンスであり、隠しフィールドと ベースクラスのフィールドは除外される。
タプル内のフィールドの順序は、フィールドが宣言された順序と一致する。
注:xml-ph-0000@deepl.internalは、xml-ph-0001@deepl.internalクラスでは利用できない。 これは、それらのフィールドが動的なオフセットを持つためである。class Foo { int x; long y; } static assert(__traits(identifier, Foo.tupleof[0]) == "x"); static assert(is(typeof(Foo.tupleof)[1] == long)); void main() { import std.stdio; auto foo = new Foo; foo.tupleof[0] = 1; // foo.xを1にセットする foo.tupleof[1] = 2; // foo.yを2にセットする foreach (ref x; foo.tupleof) x++; assert(foo.x == 2); assert(foo.y == 3); auto bar = new Foo; bar.tupleof = foo.tupleof; // フィールドをコピーする assert(bar.x == 2); assert(bar.y == 3); }
プロパティ.__vptr と.__monitor は、 それぞれクラスオブジェクトの vtbl[] と monitor へのアクセスを提供するが、 ユーザーコードでは使用すべきではない。
フィールドのプロパティ
.offsetof プロパティは、クラスのインスタンス化の開始時点からのフィールドのオフセットをバイト単位で示す。
注:xml-ph-0000@deepl.internalは、xml-ph-0001@deepl.internalクラスのフィールドには使用できない 。これは、これらのフィールドのオフセットが動的であるためである。メンバー関数(別名メソッド)
非静的メンバ関数には、 thisこれを通して、クラスオブジェクトの他のメンバーに アクセスできる。
非staticメンバ関数は、通常の FunctionAttributeに加えて、const 、immutable 、shared 、inout 、scope 、またはreturn scope の属性を持つことができる。 これらの属性は、隠しthisパラメータに適用される。
xml-ph-0000@deepl.internalclass C { int a; void foo() const { a = 3; // エラー、'this'はconstである } void foo() immutable { a = 3; // エラー、'this'は不変である } C bar() @safe scope { return this; // エラー、'this'はscopeである } }
Objective-C リンカー
Objective-C リンクを持つ静的メンバ関数には、 thisと呼ばれる追加の隠しパラメータも存在する。 このパラメータを通じて、クラスオブジェクトの他のメンバにアクセスできる。
Objective-C リンケージのメンバー関数には、 非表示の匿名パラメータが追加されており、これはその関数が呼び出されたセレクタである 。
Objective-C リンケージの static メンバ関数は、 非 static メンバ関数として隠されたネストされたメタクラスに配置される。
メソッド呼び出しの同期
(synchronized ではない)クラスのメンバー関数は、個別にsynchronized としてマークすることができる。 メソッドが呼び出されると、そのクラスのインスタンスのモニターオブジェクトがロックされ、 呼び出しが終了するとロックが解除される。
synchronizedメソッドは、クラスのインスタンス上でしか呼び出せない sharedクラスのインスタンス上でなければなりません。
class C { void foo(); synchronized int bar(); } void test(C c) { c.foo; // OK //c.bar; // エラー、`c`は`shared`ではない shared C sc = new shared C; //sc.foo; // エラー、`foo`は`shared`オブジェクトを使用して呼び出すことができない sc.bar; // OK }
同期クラス
synchronized クラスの各メンバ関数は暗黙的にsynchronized される。 staticメンバ関数は、そのクラスのクラス情報オブジェクトでsynchronizedされる。 つまり、そのsynchronizedされたクラスのすべてのstaticメンバ関数に対して、1つのモニターが使用される。 synchronizedされたクラスの非static関数では、 使用されるモニターはクラスオブジェクトの一部である。例えば:
synchronized class Foo { void bar() { ...statements... } }
モニターに関しては)次の2つは同等である。
synchronized class Foo { void bar() { synchronized (this) { ...statements... } } }注:xml-ph-0000@deepl.internalはSynchronizedStatementを使用している。
同期化されたクラスのメンバーフィールドは、publicにすることはできない。
synchronized class Foo { int foo; // エラー: publicフィールド } synchronized class Bar { private int bar; // OK }
コンストラクタ
Constructor: this Parameters MemberFunctionAttributesopt FunctionBody ConstructorTemplate
フィールドはデフォルトで、 その型のデフォルト初期化子 (通常は整数型の場合は 0、 浮動小数点数型の場合は NaN)に 初期化される。 フィールド宣言にオプションの初期化子がある場合、
xml-ph-0000@deepl.internalclass Abc { int a; // aのデフォルトのイニシャライザは0である long b = 7; // bのデフォルトのイニシャライザは7である float f; // fのデフォルトのイニシャライザはNANである }
初期化子はコンパイル時に評価される。
この初期化は、コンストラクタが呼び出される前に実行される。
コンストラクタは関数名thisで定義され、戻り値はない。
class Foo { this(int x) // Fooのコンストラクタを宣言する { ... } this() { ... } }
基底クラスの構築
ベースクラスのコンストラクションは、ベースクラスのコンストラクタをsuper という名前で呼び出すことで行われる。
class A { this(int y) { } } class B : A { int j; this() { ... super(3); // 基本コンストラクタ A.this(3)を呼び出す ... } }
デリゲートコンストラクタ
コンストラクタは、同じクラスの別のコンストラクタを呼び出すことができる。 共通の初期化を共有するためにである。 これは委譲コンストラクタと呼ばれる。
class C { int j; this() { ... } this(int i) { this(); // コンストラクタ呼び出しの委譲 j = i; } }
以下の制限が適用される。
- コンストラクタが相互に呼び出すことは違法である。
this() { this(1); } this(int i) { this(); } // 不正、循環コンストラクタ呼び出し
Implementation Defined: コンパイラは、 循環するコンストラクタ呼び出しを検出する必要はない。Undefined Behavior: 循環コンストラクタ呼び出しでプログラムが実行される場合 。 - コンストラクタのコードに委譲コンストラクタ呼び出しが含まれている場合、
コンストラクタを通るすべての実行パスは、必ず1つの
委譲コンストラクタ呼び出しを行う必要がある。
this() { a || super(); } // 不正 this() { (a) ? this(1) : super(); } // OK this() { for (...) { super(); // 不正、ループ内部 } }
- this を暗黙的または明示的に参照することは、 委譲コンストラクタ呼び出しを行う前に
- 委任コンストラクタ呼び出しはラベルの後ろに記述することはできない。
暗黙の基底クラスの構築
クラスにコンストラクタが存在しないが、基底クラスにコンストラクタが存在する場合、 デフォルトのコンストラクタが暗黙的に生成される。 形式は以下のとおりである。
this() { }
委譲コンストラクタまたはsuper の呼び出しがコンストラクタに存在しない場合、 ベースクラスにnullaryコンストラクタがある場合は、super() の呼び出しがコンストラクタの最初に挿入される。 そのベースクラスに引数が必要なコンストラクタがあり、nullaryコンストラクタがない場合は、super の呼び出しが一致する形で必要となる。
クラスのインスタンス化
クラスオブジェクトのインスタンスはNewExpression で作成される。
A a = new A(3);
scope クラスのインスタンスは スタック上に割り当てられる。
以下の手順が実行される。
- オブジェクトのストレージが割り当てられる。 これが失敗した場合、null を返すのではなく、 OutOfMemoryErrorがスローされる。 したがって、null 参照の面倒なチェックは不要である。
- 生のデータは、クラス定義で提供された値を使用して静的に初期化される。 vtbl[](仮想関数へのポインタの配列)へのポインタが割り当てられる。 コンストラクタには、 仮想関数を呼び出すことができる完全に形成されたオブジェクトが渡される。 この操作は、静的バージョンのオブジェクトを 新たに割り当てられたものにメモリコピーする
- クラスに対してコンストラクタが定義されている場合、 引数リストに一致するコンストラクタが 呼び出される。
- 委譲コンストラクタが呼び出されない場合は、基底 クラス用のデフォルトコンストラクタが呼び出される。
- コンストラクタ本体が実行される。
- クラス不変チェックが有効になっている場合、クラス不変チェックが コンストラクタの最後に呼び出される。
コンストラクタ属性
コンストラクタには、以下のメンバー関数属性のいずれかを指定できる。const 、immutable 、shared 。 修飾されたオブジェクトの構築は、実装された修飾コンストラクタに制限される。
xml-ph-0000@deepl.internalclass C { this(); // 共有でない変更可能なコンストラクタ } // 変更可能なオブジェクトを作成する C m = new C(); // 変更可能なコンストラクタを使って定数オブジェクトを作成する const C c2 = new const C(); // 変更可能なコンストラクタは不変オブジェクトを作成できない // immutable C i = new immutable C(); // 変更可能なコンストラクタは共有オブジェクトを作成できない // shared C s = new shared C();
コンストラクタは異なる属性でオーバーロードすることができる。
class C { this(); // 共有でない変更可能なコンストラクタ this() shared; // 共有の変更可能なコンストラクタ this() immutable; // 不変のコンストラクタ } C m = new C(); shared s = new shared C(); immutable i = new immutable C();
純粋コンストラクタ
コンストラクタがユニークなオブジェクトを作成できる場合(例えば、pure の場合)、 そのオブジェクトは任意の修飾子に暗黙的に変換できる。
class C { this() pure; // 定義に基づけば、これは変更可能なオブジェクトを生成する。しかし // 生成されたオブジェクトは、変更可能なグローバル・データを含むことはできない。 // したがって、生成されるオブジェクトは一意である。 this(int[] arr) immutable pure; // 定義に基づけば、これは不変オブジェクトを作成する。 // しかし、引数int[]は生成されたオブジェクトには現れないので、 // 暗黙のうちにimmutableに変換されることはない。 // また、不変なグローバル・データを格納することもできない。 // したがって、生成されたオブジェクトは一意である。 } immutable i = new immutable C(); // this() pureが呼び出される shared s = new shared C(); // this() pureが呼び出される C m = new C([1,2,3]); // this(int[]) immutable pureが呼ばれる
コンストラクタ内のフィールド初期化
コンストラクタ本体において、フィールドの代入の最初のインスタンスは その初期化である。
class C { int num; this() { num = 1; // 初期化 num = 2; // 代入 } }
フィールドの型にメソッドがある場合、 opAssign メソッドが存在する場合は、初期化には使用されません。
struct A { this(int n) {} void opAssign(A rhs) {} } class C { A val; this() { val = A(1); // valはA(1)の値に初期化される val = A(2); // val.opAssign(A(2))に書き換えられる } }
フィールドの型が変更不可能な場合、複数回の初期化は拒否される。
class C { immutable int num; this() { num = 1; // OK num = 2; // エラー: 複数のフィールドの初期化 } }
フィールドが1つのパスで初期化されている場合、すべてのパスで初期化する必要がある。
class C { immutable int num; immutable int ber; this(int i) { if (i) num = 3; // 初期化 else num = 4; // 初期化 } this(long j) { j ? (num = 3) : (num = 4); // OK j || (ber = 3); // エラー、初期化は1つのパスでのみ j && (ber = 3); // エラー、初期化は1つのパスでのみ } }
フィールドの初期化は、ループ内やラベルの後では行わないこと。
class C { immutable int num; immutable string str; this() { foreach (i; 0..2) { num = 1; // エラー: フィールドの初期化はループ内では許可されない } size_t i = 0; Label: str = "hello"; // エラー: フィールドの初期化はラベルの後では許可されない if (i++ < 2) goto Label; } }
フィールドの型がデフォルトのコンストラクションを無効にしている場合は、コンストラクタで初期化する必要がある。
xml-ph-0000@deepl.internalstruct S { int y; @disable this(); } class C { S s; this(S t) { s = t; } // OK this(int i) { this(); } // OK this() { } // エラー、sが初期化されていない }
デストラクタ
Destructor: ~ this ( ) MemberFunctionAttributesopt FunctionBody
デストラクタ関数は、以下の場合に呼び出される。
- 生存オブジェクトがガベージコレクタによって削除された場合
- 有効なscope クラスのインスタンスがスコープ外になった場合
- destroyオブジェクト上で呼び出される
例:
import std.stdio; class Foo { ~this() // Fooのデストラクタ { writeln("dtor"); } } void main() { auto foo = new Foo; destroy(foo); writeln("end"); }
- デストラクタはクラスごとに1つだけ宣言できるが、 他のデストラクタを混在させることはできる。
- デストラクタにはパラメータや属性はない。
- デストラクタは常に仮想関数である。
デストラクタは、オブジェクトが保持するGC以外のリソースを解放することが期待されている。
プログラムは、生存中のオブジェクトのデストラクタを明示的に即座に呼び出すことができる 。 destroy。 ランタイムはオブジェクトにマークを付けるので、デストラクタが二度呼び出されることはない。
スーパークラスのデストラクタは、デストラクタが終了した時点で自動的に呼び出される。 スーパークラスのデストラクタを明示的に呼び出す方法はない。 xml-ph-0000@deepl.internal
スタティックデータセグメントから参照されたオブジェクトは、 GCによって収集されることはない。
静的コンストラクタ
StaticConstructor: static this ( ) MemberFunctionAttributesopt FunctionBody
静的コンストラクタとは、main() 関数がメインスレッドの制御を取得する前に、スレッドローカルデータの初期化を実行する関数である。 また、スレッドの起動時にも実行される。
静的コンストラクタは、静的クラスメンバを、 コンパイル時に計算できない値で初期化するために使用される。
class Foo { static int a = b + 1; static int b = a * 2; }a とb が最終的にどのような値になるのか、初期化がどのような順序で実行されるのか、 初期化が実行される前にa とb の値はどのようなものなのか、これは コンパイルエラーなのか、それとも ランタイムエラーなのか? さらに、初期化子が staticなのかdynamicなのかが 明らかでないという点も混乱を招いている。
D はこれをシンプルにする。すべてのメンバー初期化は、 コンパイル時に コンパイラによって決定可能でなければならない。したがって、メンバー初期化には評価順序の依存関係はなく、 初期化されていない 値を読み取ることはできない。動的 初期化は、特別な構文で定義された静的コンストラクタによって実行されるstatic this() 。
class Foo { static int a; // デフォルトは0に初期化される static int b = 1; static int c = b + a; // エラー、定数イニシャライザではない static this() // 静的コンストラクタ { a = b + 1; // aは2にセットされる b = a * 2; // bは4に設定される } }xml-ph-0000@deepl.internal
main() またはスレッドが正常に終了した場合(例外がスローされない場合)、 スレッドの終了時に呼び出される関数のリストに静的デストラクタが追加される。
静的コンストラクタのパラメータリストは空である。
モジュール内の静的コンストラクタは、 現れた順に語彙順で 実行される。直接または間接的にインポートされたモジュール用の静的コンストラクタはすべて、 インポータ用の静的コンストラクタの前に 実行される。
静的コンストラクタ宣言におけるstatic は属性ではなく、this の直前に記述する必要がある。
class Foo { static this() { ... } // 静的コンストラクタ static private this() { ... } // エラー: 静的コンストラクタではない static { this() { ... } // 静的コンストラクタではない } static: this() { ... } // 静的コンストラクタではない }
静的デストラクタ
StaticDestructor: static ~ this ( ) MemberFunctionAttributesopt FunctionBody静的デストラクタは、特別な静的関数として定義され、 構文はstatic ~this() である。
class Foo { static ~this() // 静的デストラクタ { } }
静的デストラクタはスレッドの終了時に呼び出されるが、 静的コンストラクタが 正常に完了した場合のみである。 静的デストラクタのパラメータリストは空である。 静的デストラクタは、静的コンストラクタが呼び出された順序の逆順で呼び出される。
静的デストラクタ宣言におけるstatic は、 属性ではなく、~this の直前に記述する必要がある。
class Foo { static ~this() { ... } // 静的デストラクタ static private ~this() { ... } // エラー: 静的デストラクタではない static { ~this() { ... } // 静的デストラクタではない } static: ~this() { ... } // 静的デストラクタではない }
共有静的コンストラクタ
SharedStaticConstructor: shared static this ( ) MemberFunctionAttributesopt FunctionBody
共有静的コンストラクタは、いかなるStaticConstructorよりも先に実行され、 共有グローバルデータの初期化を目的としている。
共有静的デストラクタ
SharedStaticDestructor: shared static ~ this ( ) MemberFunctionAttributesopt FunctionBody
共有静的デストラクタは、プログラムの終了時に SharedStaticConstructorsが実行されたのとは逆の順序で 実行される。
クラスの不変条件
Invariant: invariant ( ) BlockStatement invariant BlockStatement invariant ( AssertArguments ) ;
クラス不変量は、クラスインスタンスのメンバー間の関係を指定する。 それらの関係は、そのインスタンスの公開インターフェースからのあらゆるやり取りに対して保持されなければならない。
不変条件は、const メンバ関数の形式で記述される。 不変条件が満たされるのは、その不変条件の中で実行されるすべてのAssertExpressionsが 成功した場合である。
class Date { this(int d, int h) { day = d; // daysは1..31 hour = h; // hoursは0..23 } invariant { assert(1 <= day && day <= 31); assert(0 <= hour && hour < 24); } private: int day; int hour; }
ベースクラスのクラス不変条件は、派生クラスのクラス不変条件の前に適用される。
クラスには複数の不変条件が存在する場合がある。これらは語彙順に適用される。
クラス不変条件は、クラスコンストラクタ(存在する場合)の終了時、および クラスデストラクタ(存在する場合)の開始時に保持されなければならない。
クラス不変条件は、 すべてのパブリックまたはエクスポートされた非staticメンバ関数のエントリと出口で保持されなければならない。 不変条件の適用順序は以下のとおりである。
前提条件- 前提条件
- 不変
- 関数本体
- 不変
- 事後条件
不変条件が満たされない場合、プログラムは無効な状態になる。
- クラス不変条件が実行されるかどうかは、実行時であるか否か。これは通常、 コンパイラのスイッチで制御される。
- 不変条件が満たされない場合の動作は、通常、 AssertExpressionsが失敗した場合と同じである。
パブリックまたはエクスポートされた非スタティックメンバ関数は、不変条件の範囲内から呼び出すことはできない。
class Foo { public void f() { } private void g() { } invariant { f(); // エラー、不変量からパブリック・メンバ関数を呼び出すことはできない g(); // OK、g()はpublicではない } }
- クラス不変値の中で、間接的にエクスポートされた関数やパブリックメンバ関数を呼び出すことはしないこと。 無限再帰を引き起こす可能性があるため。
- 不変条件に副作用を頼らないようにすること。不変条件が実行されるとは限らないため 。
- 不変条件を持つクラスの変更可能なパブリックフィールドを持つことは避けるべきである。 そうすると、不変条件がパブリックインターフェースを検証できなくなるからだ。
スコープクラス
スコープクラスとは、scope 属性を持つクラスのことである。例えば、
scope class Foo { ... }
スコープの特性は継承されるため、スコープクラスから派生したクラスはすべて スコープとなる。
スコープクラスの参照は、関数ローカル変数としてのみ使用できる。scope として宣言する必要がある。
scope class Foo { ... } void func() { Foo f; // エラー、scopeクラスへの参照はscopeでなければならない scope Foo g = new Foo(); // 正しい }
スコープクラスの参照がスコープ外に出ると、そのデストラクタ(存在する場合)が 自動的に呼び出される。これは、例外がスローされてスコープが終了した場合でも同様である。
抽象クラス
抽象的なメンバ関数は、派生クラスでオーバーライドする必要がある。 仮想メンバ関数のみを抽象として宣言できる。非仮想 メンバ関数および独立した関数は、抽象として宣言できない。
クラスが抽象クラスとなるのは、そのクラスの仮想メンバ関数のいずれかが 抽象関数として宣言されている場合、または仮想メンバ関数が抽象属性内に定義されている場合である 。 抽象クラスには、仮想でないメンバ関数も含めることができる点に注意すること。 抽象クラスは直接インスタンス化することはできない。 抽象クラスは、別の非抽象クラスのベースクラスとしてのみインスタンス化できる 。
class C { abstract void f(); } auto c = new C; // エラー、Cはabstractである class D : C {} auto d = new D; // エラー、Dはabstractである class E : C { override void f() {} } auto e = new E; // OK
抽象関数として宣言されたメンバ関数には、関数本体を定義することができる。 これは、オーバーライドする必要がある場合でも、 派生クラスで「ベースクラスの機能」を提供できるようにするためである。 super.foo()派生クラスで。 注釈:クラスは依然として抽象クラスであり、直接インスタンス化することはできない。
クラスを抽象クラスとして宣言できる。
abstract class A { // ... } auto a = new A; // エラー、Aはabstractである class B : A {} auto b = new B; // OK
最終クラス
最終クラスは分割できない。
final class A { } class B : A { } // エラー、クラスAはfinalである
最終クラスのメソッドは常に final。
ネストされたクラス
ネストされたクラスとは、関数または他のクラスのスコープ内で宣言されたクラスのことである。 ネストされたクラスは、ネストされたクラスが含まれる関数や他のクラスの変数やシンボルにアクセスできる。
class Outer { int m; class Inner { int foo() { return m; // Outerのメンバにアクセスしてもよい } } }xml-ph-0000@deepl.internal
void func() { int m; class Inner { int foo() { return m; // func()のローカル変数mへのアクセスはOK } } }
静的ネストクラス
ネストされたクラスがstatic 属性を持つ場合、 スタックにローカルな変数や、this 参照を必要とする
class Outer { int m; static int n; static class Inner { int foo() { return m; // エラー、Innerはstaticであり、mはthisが必要である return n; // OK、nはstaticである } } }xml-ph-0000@deepl.internal
void func() { int m; static int n; static class Inner { int foo() { return m; // エラー、Innerはstaticで、mはスタックのローカル変数である return n; // OK、nはstaticである } } }
コンテクストポインタ
非静的ネストされたクラスは、追加の隠しメンバ( コンテキストポインタと呼ばれる)を含むことで機能する。このコンテキストポインタは、 関数内にネストされている場合は、その関数のフレームポインタであり、クラス内にネストされている場合は、そのクラスを囲むクラスのインスタンスのthis 参照である。
非静的ネストクラスがインスタンス化される際には、コンテキストポインタが クラスのコンストラクタが呼び出される前に割り当てられるため、 コンストラクタは外側の変数に完全にアクセスできる。 非静的ネストクラスは、必要なコンテキストポインタの情報が利用可能である場合にのみインスタンス化できる。
class Outer { class Inner { } static class SInner { } } void main() { Outer o = new Outer; // OK //Outer.Inner oi = new Outer.Inner; // エラー、Outerに'this'がない Outer.SInner os = new Outer.SInner; // OK }
void main() { class Nested { } Nested n = new Nested; // OK static f() { //Nested sn = new Nested; // エラー、Nestedの'this'がない } }
明示的インスタンス化
this 参照は、 NewExpressionの前に付けることで、
class Outer { int a; class Inner { int foo() { return a; } } } void main() { Outer o = new Outer; o.a = 3; Outer.Inner oi = o.new Inner; assert(oi.foo() == 3); }
ここで、o は、Outer の内部クラスインスタンスへの参照であるthis を指定する。
outer プロパティ
ネストされたクラスインスタンスの場合、.outer プロパティは、 囲むクラスのインスタンスのthis 参照を提供する。 アクセス可能な親クラスインスタンスがない場合、プロパティは 囲む関数フレームにvoid* を提供する。
class Outer { class Inner1 { Outer getOuter() { return this.outer; } } void foo() { Inner1 i = new Inner1; assert(i.getOuter() is this); } }xml-ph-0002@deepl.internalxml-ph-0000@deepl.internal
class Outer { void bar() { // xは入れ子のスコープから参照されるので、 //barはクロージャ環境を作る。 int x = 1; class Inner2 { Outer getOuter() { x = 2; // Inner2インスタンスは、staticフレームポインタとして // barの関数フレームにアクセスできるが、 //.outerは囲むOuterクラスのインスタンス・プロパティを返す。 return this.outer; } } Inner2 i = new Inner2; assert(i.getOuter() is this); } }xml-ph-0000@deepl.internal
class Outer { // bazはOuterのインスタンスにアクセスできない static void baz() { // クロージャ環境を作る int x = 1; class Inner3 { void* getOuter() { x = 2; // アクセス可能な囲みクラス・インスタンスはないので、 // .outerプロパティはbazの関数フレームを返す。 return this.outer; } } Inner3 i = new Inner3; assert(i.getOuter() !is null); } }
匿名ネストクラス
匿名のネストされたクラスは、NewAnonClassExpressionで定義およびインスタンス化される。 xml-ph-0000@deepl.internal
NewAnonClassExpression: new class ConstructorArgsopt AnonBaseClassListopt AggregateBody ConstructorArgs: ( NamedArgumentListopt ) AnonBaseClassList: SuperClassOrInterface SuperClassOrInterface , Interfacesこれは、次の式と同等である。
class Identifier : AnonBaseClassList AggregateBody // ... new Identifier ConstructorArgsここで、Identifierは匿名ネストクラスに生成された名前である。
interface I { void foo(); } auto obj = new class I { void foo() { writeln("foo"); } }; obj.foo();
定数、不変、共有クラス
ClassDeclarationがconst 、immutable またはshared ストレージクラスを持つ場合、そのクラスの各メンバーは そのストレージクラスで宣言されたものとみなされる。 ベースクラスがconst、immutable、またはsharedの場合、そのベースクラスから派生するすべてのクラスも const、immutable、またはsharedとなる。
DEEPL APIにより翻訳、ところどころ修正。
このページの最新版(英語)
このページの原文(英語)
翻訳時のdmdのバージョン: 2.109.1
ドキュメントのdmdのバージョン: 2.109.1
翻訳日付 :
HTML生成日時:
編集者: dokutoku