最初のコードは多くの作業を行います。
void useFoo() {
Foo f = makeFoo(); // line 2
f.doSomething();
}
2行目を考えると、いくつかの興味深いことが起こります。まず、コンパイラは、クラスのデフォルトコンストラクタを使用してFoo
オブジェクトを構築するためのコードを発行します。f
次に、を呼び出しますmakeFoo()
。これにより、新しいFoo
オブジェクトが作成され、そのオブジェクトへの参照が返されます。makeFoo()
コンパイラはまた、の一時的な戻り値をオブジェクトにコピーするコードを発行する必要がありf
ます。そうすると、一時的なオブジェクトが破棄されます。2行目が完了すると、f.doSomething()
が呼び出されます。ただし、オブジェクトがスコープ外になっているため、戻る直前にuseFoo()
、でオブジェクトも破棄します。f
2番目のコード例ははるかに効率的ですが、実際にはおそらく間違っています。
void useFoo() {
Foo& f = makeFoo(); // line 2
f.doSomething();
}
f
その例の2行目を考えると、これは単なる参照であるため、オブジェクトを作成しないことがわかります。このmakeFoo()
関数は、新しく割り当てられたオブジェクトを返し、そのオブジェクトへの参照を保持します。私たちはdoSomething()
その参照を通して呼びます。しかし、useFoo()
関数が戻ったときに、作成した新しいオブジェクトを破棄することはなく、makeFoo()
リークします。
これを修正する方法はいくつかあります。追加のコンストラクター、作成、コピー、および破棄を気にしない場合は、最初のコードフラグメントにある参照メカニズムを使用できます。(些細なコンストラクタとデストラクタがあり、コピーする状態があまりない(またはまったくない)場合は、それほど重要ではありません。)ポインタを返すだけで済みます。これは、呼び出し元がライフの管理に責任があることを強く示唆しています。参照されるオブジェクトのサイクル。
ポインタを返す場合は、呼び出し元がオブジェクトのライフサイクルを管理する必要があることを意味していますが、それを強制していません。誰か、いつか、どこかでそれを間違えるでしょう。したがって、参照を管理し、オブジェクトの管理をカプセル化するためのアクセサーを提供するラッパークラスを作成することを検討してください。(必要に応じて、それをクラス自体に組み込むこともできFoo
ます。)このタイプのラッパークラスは、一般的な形式では「スマートポインター」と呼ばれます。STLを使用している場合は、 std::unique_ptr
テンプレートクラスにスマートポインタの実装があります。