24891


現在C++を勉強中なのですが、unique_ptrの使い方でわからないことがあります。  
オブジェクトのポインタを渡して、unique_ptrを生成する際、C++14であればヘルパー関数を使うことができると思います。しかし、そのままnewを使う場合と比べてヘルパー関数を使う利点がわかりません。

例えば、下記の1点目と比較して、2点目はどのような利点があるのでしょうか？

  * `std::unique_ptr<int> pInt {new int{1}};`
  * `std::unique_ptr<int> pInt {std::make_unique<int>(1)};`


CC BY-SA 3.0
2016-05-15T08:36:22.160
24876
2016-05-16T01:14:29.570
2016-05-15T09:42:30.837
8000
15485
question
c++
make_uniqueの利点


`make_unique`を使用するとコンストラクター呼び出しではなくなるので`auto`変数が使えます。対して`std::unique_ptr`はコンストラクター呼び出しなので型名を省略できません。

[code]

    std::unique_ptr<int> pInt {new int{1}};
    auto pInt = std::make_unique<int>(1);
    
[/code]

また前者は`std::unique_ptr`でポインター管理され自動的に`delete`されるとは言え、ソースコードの見た目上は`new`しておいて`delete`していないという非対称な構造になってしまいます。`make_unique`であればそのようなことにはなりません。

* * *

逆に`make_unique`はカスタム`Deleter`を指定できないという欠点があります。


CC BY-SA 3.0
2016-05-15T21:58:41.867
24886
2016-05-15T21:58:41.867
4236
24876
answer

[code]

    std::unique_ptr<int> pInt {new int{1}}; // (1)
    
[/code]

と

[code]

    std::unique_ptr<int> pInt {std::make_unique<int>(1)};  // (2)
    
[/code]

の大きな違いは、`unique_ptr<int>` のインスタンスを作るのに、(1) では `new int{1}`
が呼ばれてから、`unique_ptr<int>` のコンストラクタが呼ばれるという風に、2段階で処理が行われるのに対し、(2)
では、`std::make_unique<int>(1)` を呼ぶだけで作られるということです。

例えば

[code]

    class A;
    
    void f(std::unique_ptr<A> p1, std::unique_ptr<A> p2);
    
[/code]

という関数があったときに、

[code]

    f(std::unique_ptr<A>{new A{0}}, std::unique_ptr<A>{new A{1}}); // (3)
    
[/code]

と呼び出したとします。ここで生成された二つの A のインスタンスは、`unique_ptr`
になるので、きちんと解放されるように見えます。しかし、実は解放されないケースがありうるのです。

C++ では、関数の引数の評価の順番は未規定です。(3) の場合だと

  1. `new A{0}`
  2. `new A{1}`
  3. 1 で生成した A のインスタンスを用いて、一番目の `std::unique_ptr<A>` を生成
  4. 2 で生成した A のインスタンスを用いて、二番目の `std::unique_ptr<A>` を生成
  5. `f()` の呼び出し

という順番で評価しても構わないのです。このとき 2. で A のコンストラクタが例外を投げると、1. で生成された A
のインスタンスは、まだunique_ptr になっていないので、リークしてしまいます。

ここで `std::make_unique` を使えば、

[code]

    f(std::make_unique<A>(0), std::make_unique<A>(1)); // (4)
    
[/code]

  1. `std::make_unique<A>(0)` で一番目の `unique_ptr<A>` を生成
  2. `std::make_unique<A>(1)` で二番目の `unique_ptr<A>` を生成
  3. `f()` の呼び出し

となり、`new A{1}` が例外を発生する物であっても、メモリリークを起こすことがありません。


CC BY-SA 3.0
2016-05-16T01:14:29.570
24891
2016-05-16T01:14:29.570
3605
24876
answer