確かに、次の間に実質的な違いがあります。
shared_ptr<T> sp(new T());
と:
shared_ptr<T> sp = make_shared<T>();
最初のバージョンはT
オブジェクトの割り当てを実行し、次に別の割り当てを実行して参照カウンターを作成します。2番目のバージョンは、オブジェクトと参照カウンターの両方に対して単一の割り当てを実行し、それらをメモリの連続した領域に配置して、メモリのオーバーヘッドを減らします。
また、一部の実装では、次の場合にさらにスペースの最適化を実行できますmake_shared<>
(MSの実装によって行われる「WeKnow Where YouLive」の最適化を参照)。
しかし、それが存在する唯一の理由ではありませんmake_shared<>
。明示に基づくバージョンnew T()
は、状況によっては例外安全ではありません。特に、を受け入れる関数を呼び出す場合はそうshared_ptr
です。
void f(shared_ptr<T> sp1, shared_ptr<T> sp2);
...
f(shared_ptr<T>(new T()), shared_ptr<T>(new T()))
ここで、コンパイラは最初のnew T()
式を評価し、次に2番目のnew T()
式を評価してから、対応するshared_ptr<>
オブジェクトを作成できます。しかし、最初に割り当てられたオブジェクトがそのオブジェクトにバインドされる前に、2番目の割り当てによって例外が発生した場合はどうなりますshared_ptr<>
か?漏れます。を使用make_shared<>()
すると、これは不可能です。
f(make_shared<T>(), make_shared<T>())
shared_ptr<>
割り当てられたオブジェクトは、への各関数呼び出し内のそれぞれのオブジェクトにバインドされるためmake_shared<>()
、この呼び出しは例外安全です。new
これは、自分が何をしているのかを本当に理解していない限り、裸を使用してはならないもう1つの理由です。(*)
についてのあなたの意見を考慮すると、rawポインターが引数として渡されたときに、newの構築が別々の割り当てを実行するのと同じように、カウンターとオブジェクトに対して別々の割り当てを実行reset()
することを正しく観察しています。したがって、を使用した割り当てが推奨されます(またはなどのステートメントでも)。reset(new T())
shared_ptr<>
make_shared<>
reset(make_shared<T>())
reset()
可変引数リストをサポートする必要があるかどうかにかかわらず、これはおそらく、StackOverflowが適切ではない一種のオープンディスカッションです。
(*)まだそれを必要とするいくつかの状況があります。たとえば、C ++標準ライブラリには対応するmake_unique<>
関数がないためunique_ptr
、自分で関数を作成する必要があります。もう1つの状況は、オブジェクトとカウンターを単一のメモリブロックに割り当てたくない場合です。これは、オブジェクトへの弱いポインターが存在すると、オブジェクトへの所有ポインターがなくなっても、ブロック全体の割り当てが解除されないためです。 。