ネストされた関数、構造体、およびクラス
これまで、関数、構造体、およびクラスは、最外側のスコープ (つまり、モジュールスコープ)で定義してきた。これらは、内側のスコープでも定義することができる。内側のスコープで定義すると、シンボルの可視性を狭めることでカプセル化に役立ち、また、関数ポインタ、デリゲート、およびラムダの章で見たクロージャを作成することができる。
例として、次のouterFunc()関数には、ネストされた関数、ネストされたstruct、およびネストされたclassの定義が含まれている。
他の変数と同様に、ネストされた定義は、その外側のスコープで定義されているシンボルにアクセスできる。例えば、上記の3つのネストされた定義はすべて、parameterおよびlocalという名前の変数を使用できる。
通常どおり、ネストされた定義の名前は、それらが定義されているスコープ内でのみ有効だ。例えば、nestedFunc()、NestedStruct、およびNestedClassは、main()からはアクセスできない。
その名前にはアクセスできないが、ネストされた定義は他のスコープでは使用できる。例えば、Phobosのアルゴリズムの多くは、Phobos関数内で定義されたネストされた構造体によってタスクを処理している。
この例を見るために、両端を交互に消費するスライスを消費する関数を設計しよう。
ネストされたstructはmain()内では名前を付けることができないが、依然として使用可能だ:
[1, 5, 2, 4, 3]
注釈:これらの型は、そのスコープ外ではその名前を使用できないため、ハリー・ポッターの登場人物にちなんで"ヴォルデモート型"と呼ばれている。
alternatingEnds()が返すネストされたたstructには、メンバー変数は一切ないことに注意。structは、関数パラメータsliceとローカル関数変数isFromFrontのみを使用してそのタスクを処理する。返されたオブジェクトは、それが作成されたコンテキストを離れた後も、これらの変数を安全に使用できるのは、自動的に作成されクロージャによるものである。クロージャについては、関数ポインタ、デリゲート、およびラムダの章で説明した。
staticクロージャが不要な場合
ネストされた定義はコンテキストを存続させるため、通常の定義よりもコストがかかる。さらに、関連付けられているコンテキストを決定するためのコンテキストポインタを含める必要があるため、ネストされた定義のオブジェクトはより多くのスペースを占める。例えば、次の2つの構造体はまったく同じメンバー変数を持っているが、そのサイズは異なる。
他の環境では、2つの構造体のサイズが異なる場合もある:
| 外側の構造体 | ネストされた構造体 |
|---|---|
| 4バイト | 16バイト |
ただし、一部のネストされた定義は、外側のコンテキストの変数にアクセスする必要がなく、可能な限りローカルに保持するためだけに使用される。このような場合、関連するコストは不要である。staticキーワードは、ネストされた定義からコンテキストポインタを削除し、モジュール内の対応する定義と等価にする。その結果、staticネストされた定義は外側のコンテキストにアクセスできない:
ネストされたclassオブジェクトのコンテキストポインタは、その.outerプロパティを通じてvoid*として利用できる。例えば、同じスコープで定義されているため、次の2つのオブジェクトのコンテキストポインタは等しい。
後で説明するように、クラス内にネストされたクラスでは、コンテキストポインタの型は、void*ではなく、外側のクラスの型になる。
クラス内にネストされたクラス
classが別の 内にネストされている場合、ネストされたオブジェクトが関連付けられるコンテキストは、外側のオブジェクト自体になる。
このようなネストされたクラスは、this.new構文で構築される。必要に応じて、ネストされたオブジェクトの外側のオブジェクトはthis.outerでアクセスできる:
this.newとthis.outerの代わりに、既存のオブジェクトに対して.newと.outerを使用することもできる:
要約
- 内部スコープで定義された関数、構造体、およびクラスは、それらのスコープをコンテキストとしてアクセスできる。
- ネストされた定義は、コンテキストを保持してクロージャを形成する。
- ネストされた定義は、モジュール内の定義よりもコストが高い。ネストされた定義がコンテキストにアクセスする必要がない場合、
staticキーワードを使用することでこのコストを回避できる。 - クラスは他のクラス内にネストできる。このようなネストされたオブジェクトのコンテキストは、外側のオブジェクト自体だ。ネストされたクラスオブジェクトは、
this.newまたはvariable.newで構築され、そのコンテキストはthis.outerまたはvariable.outerでアクセスできる。