36

私はC++11の新しいイディオムに頭を悩ませようとしています。

new T()少なくともshared_ptrでは、とを使用することには実質的な違いがあるようですmake_shared<T>()

しかし、何かの新しいインスタンスを指すように共有ポインタをリセットするのはどうでしょうか。以前は、通常、reset(new T())メンバーを使用していました。しかし、これはそもそもmake_shared()を使用しないのと同じ問題に悩まされていませんか?(つまり、make_sharedがオブジェクトを割り当てることを許可しないため、参照カウントをT自体と同じ割り当てではなく、別の割り当てに配置する必要がありますか?)

使用する方が単純に良いですか?

   mysharedptr = make_shared<T>(args...);

それとももっと良い方法はありますか?

また、make_sharedのように引数の可変個引数転送をリセットして、mysharedptr.reset(args ...);と記述できるようにするべきではありませんか?

4

2 に答える 2

40

確かに、次の間に実質的な違いがあります。

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つの状況は、オブジェクトとカウンターを単一のメモリブロックに割り当てたくない場合です。これは、オブジェクトへの弱いポインターが存在すると、オブジェクトへの所有ポインターがなくなっても、ブロック全体の割り当てが解除されないためです。 。

于 2013-02-12T16:36:05.697 に答える
6

正解です、 ;reset(new T...)のすべての問題に苦しんでいます。shared_ptr(new T...)これにより、割り当てが二重になり、例外安全性もなくなります(bad_alloc内で発生しない限り、リークの可能性はほとんどありませんreset)。

resetはと同等として文書化されているshared_ptr<T>(ptr).swap(*this)ため、次のように書くこともできます。

make_shared<T>(args...).swap(mysharedptr);

からの割り当てmake_shared<T>はほぼ同等ですが、唯一の違いは、古いものの削除とT一時的なものの破棄の相対的な順序でありshared_ptr、これは観察できません。

于 2013-02-12T16:41:13.450 に答える