ネストされた関数、構造体、およびクラス
これまで、関数、構造体、およびクラスは、最外側のスコープ (つまり、モジュールスコープ)で定義してきた。これらは、内側のスコープでも定義することができる。内側のスコープで定義すると、シンボルの可視性を狭めることでカプセル化に役立ち、また、関数ポインタ、デリゲート、およびラムダの章で見たクロージャを作成することができる。
例として、次の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
でアクセスできる。