26

次の 2 行のコードが同じかどうかに興味があります。

shared_ptr<int> sp(new int(1)); // double allocation?
shared_ptr<int> sp(make_shared<int>(1)); // just one allocation?

これが本当なら、2 行目に割り当てが 1 つしかない理由を誰か説明してもらえますか?

4

3 に答える 3

32

最初のケースは二重割り当てを実行せず、2 つの割り当てを実行します。1 つは管理対象オブジェクト用で、もう 1 つは の制御ブロック用ですshared_ptr

2 番目のケースでは、cppreferenceには、 std::make_shared が通常、それが言う 1 つのメモリ割り当てのみを実行する理由についての適切な説明があります (私のものは今後も強調されます)。

この関数は通常、T オブジェクトと shared_ptr の制御ブロックに単一のメモリ割り当てでメモリを割り当てます(標準では拘束力のない要件です)。対照的に、宣言 std::shared_ptr p(new T(Args...)) は少なくとも 2 つのメモリ割り当てを実行するため、不要なオーバーヘッドが発生する可能性があります。

std::shared_ptrセクションから、次のように書かれています。

std::make_shared または std::allocate_shared を呼び出して shared_ptr を作成すると、制御ブロックと管理対象オブジェクトの両方のメモリが 1 回の割り当てで作成されます。管理対象オブジェクトは、制御ブロックのデータ メンバーにインプレースで構築されます。shared_ptr コンストラクターの 1 つを使用して shared_ptr を作成する場合、管理対象オブジェクトと制御ブロックを別々に割り当てる必要があります。この場合、制御ブロックは管理対象オブジェクトへのポインタを格納します。

この説明は、セクションshared_ptr の作成で述べているC++11 ドラフト標準make_sharedと一致しています。20.7.2.2.6

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);
template<class T, class A, class... Args>
  shared_ptr<T> allocate_shared(const A& a, Args&&... args);

[...]

備考: 実装では、メモリ割り当てを 1 回だけ実行する必要があります。[ 注: これにより、侵入型のスマート ポインターと同等の効率が得られます。—終わりのメモ]

[ 注: これらの関数は通常、参照カウントなどの内部簿記構造を可能にするために sizeof(T) よりも多くのメモリを割り当てます。—終わりのメモ]

Herb Sutter は、 GotW #89 Solution: Smart Pointersmake_sharedで使用する利点についてより詳細に説明し、いくつかの利点を指摘しています。

  • 割り当てのオーバーヘッドを削減します
  • 地域性を高めます。
  • 明示的な new を回避します。
  • 例外の安全性の問題を回避します。

make_shared を使用してstd::weak_ptrを使用する場合 、いくつかの欠点があることに注意してください。

于 2014-07-16T11:46:59.560 に答える
1

微妙なバグの可能性もありsp(new int)ます。最初に int を割り当てます (そのポインタは に与えられますsp)。 sp 自体が制御ブロックを割り当てなければなりません (カウンターとデリータが含まれます)。

ここで、この最後の割り当てspが失敗した場合 (メモリ不足)、ヒープに割り当てられた int が残ります。そのポインターは誰にも保持されていないため、削除できません。(メモリーリーク)。

于 2014-07-17T09:50:26.017 に答える