質問: 現在C++を勉強中なのですが、unique_ptrの使い方でわからないことがあります。 オブジェクトのポインタを渡して、unique_ptrを生成する際、C++14であればヘルパー関数を使うことができると思います。しかし、そのままnewを使う場合と比べてヘルパー関数を使う利点がわかりません。 例えば、下記の1点目と比較して、2点目はどのような利点があるのでしょうか? * `std::unique_ptr pInt {new int{1}};` * `std::unique_ptr pInt {std::make_unique(1)};`
回答: ``` std::unique_ptr pInt {new int{1}}; // (1) ``` と ``` std::unique_ptr pInt {std::make_unique(1)}; // (2) ``` の大きな違いは、`unique_ptr` のインスタンスを作るのに、(1) では `new int{1}` が呼ばれてから、`unique_ptr` のコンストラクタが呼ばれるという風に、2段階で処理が行われるのに対し、(2) では、`std::make_unique(1)` を呼ぶだけで作られるということです。 例えば ``` class A; void f(std::unique_ptr p1, std::unique_ptr p2); ``` という関数があったときに、 ``` f(std::unique_ptr{new A{0}}, std::unique_ptr{new A{1}}); // (3) ``` と呼び出したとします。ここで生成された二つの A のインスタンスは、`unique_ptr` になるので、きちんと解放されるように見えます。しかし、実は解放されないケースがありうるのです。 C++ では、関数の引数の評価の順番は未規定です。(3) の場合だと 1. `new A{0}` 2. `new A{1}` 3. 1 で生成した A のインスタンスを用いて、一番目の `std::unique_ptr` を生成 4. 2 で生成した A のインスタンスを用いて、二番目の `std::unique_ptr` を生成 5. `f()` の呼び出し という順番で評価しても構わないのです。このとき 2. で A のコンストラクタが例外を投げると、1. で生成された A のインスタンスは、まだunique_ptr になっていないので、リークしてしまいます。 ここで `std::make_unique` を使えば、 ``` f(std::make_unique(0), std::make_unique(1)); // (4) ``` 1. `std::make_unique(0)` で一番目の `unique_ptr` を生成 2. `std::make_unique(1)` で二番目の `unique_ptr` を生成 3. `f()` の呼び出し となり、`new A{1}` が例外を発生する物であっても、メモリリークを起こすことがありません。