[解決済み] C++のmake_sharedと通常のshared_ptrの違いについて
質問
std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));
この件に関しては、googleやstackoverflowに多くの投稿がありますが、私はなぜ理解できないのでしょうか?
make_shared
を直接使用するよりも、より効率的です。
shared_ptr
.
誰か、オブジェクトの作成と操作の順序をステップバイステップで説明してくれませんか?
make_shared
は効率的です。参考までに、上に1つの例を挙げました。
どのように解決するのですか?
その差は
std::make_shared
はヒープを 1 つ確保するのに対して
std::shared_ptr
コンストラクタは2回実行されます。
ヒープの確保はどこで行われるのですか?
std::shared_ptr
は2つの実体を管理します。
- 制御ブロック(参照カウント、タイプ消去されたデレタなどのメタデータを格納します。)
- 管理対象オブジェクト
std::make_shared
は、制御ブロックとデータの両方に必要な領域を考慮した単一のヒープ割り当てを実行します。もう一方の場合は
new Obj("foo")
は管理されたデータに対してヒープ割り当てを行い
std::shared_ptr
コンストラクタは制御ブロックのために別のものを実行します。
さらに詳しい情報は 実装ノート で 参照 .
アップデート I: 例外-安全性
ノート (2019/08/30) : C++17以降では、関数の引数の評価順序が変更されたため、この問題は発生しません。具体的には、関数の各引数は、他の引数の評価の前に完全に実行されることが必要です。
OPは例外安全性の側面について質問しているようなので、私の回答を更新しました。
この例で考えてみましょう。
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
C++では部分式の評価順序を任意に設定できるため、考えられる順序の1つは以下の通りです。
-
new Lhs("foo"))
-
new Rhs("bar"))
-
std::shared_ptr<Lhs>
-
std::shared_ptr<Rhs>
さて、ステップ2で例外が発生したとします(例:メモリ不足の例外。
Rhs
コンストラクタが何らかの例外をスローした場合。) そうすると、ステップ 1 で割り当てたメモリは、何もクリーンアップする機会がないため、失われます。この問題の核心は、生のポインタが
std::shared_ptr
のコンストラクタですぐに実行します。
これを解決する一つの方法は、この恣意的な順序付けが起こらないように、それらを別々の行で行うことです。
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
もちろん、これを解決する好ましい方法は
std::make_shared
の代わりに
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
更新情報II:デメリットについて
std::make_shared
引用元 ケーシー のコメントです。
割り当てが1回しかないので、コントロールブロックが使用されなくなるまでポインティのメモリは割り当て解除できません。A
weak_ptr
は、コントロールブロックを無限に生かすことができます。
のインスタンスは、なぜ
weak_ptr
はコントロールブロックを生かしますか?
の方法があるはずです。
weak_ptr
は、管理対象オブジェクトがまだ有効であるかどうかを判断するために(例.
lock
). これを行うには
shared_ptr
は、コントロールブロックに格納されている管理対象オブジェクトを所有する。その結果、コントロールブロックが生きている間は
shared_ptr
をカウントし
weak_ptr
のカウントがともに0になりました。
戻る
std::make_shared
以降
std::make_shared
はコントロールブロックとマネージドオブジェクトの両方に対して単一のヒープ割り当てを行うため、コントロールブロックとマネージドオブジェクトのメモリを別々に解放する方法がありません。コントロールブロックと管理されたオブジェクトの両方が解放されるまで待つ必要があります。
shared_ptr
または
weak_ptr
が生きている。
その代わりに、制御ブロックと管理オブジェクトの2つのヒープ割り当てを、以下の方法で行ったとします。
new
と
shared_ptr
のコンストラクタを使用します。そして、管理対象オブジェクトのメモリを解放します(たぶんもっと早く)。
shared_ptr
が存在しなくなったら制御ブロックのメモリを解放する(たぶん後)。
weak_ptr
が生きている。
関連
-
[解決済み】クラステンプレートの引数リストがない
-
[解決済み】エラー:strcpyがこのスコープで宣言されていない
-
[解決済み】なぜ、サイズ8の初期化されていない値を使用するのでしょうか?
-
[解決済み】Enterキーを押して続行する
-
[解決済み] 1ビットのセット、クリア、トグルはどのように行うのですか?
-
[解決済み] static_cast, dynamic_cast, const_cast, reinterpret_cast はいつ使うべきですか?
-
[解決済み] C++11では、標準化されたメモリモデルが導入されました。その意味するところは?そして、C++プログラミングにどのような影響を与えるのでしょうか?
-
[解決済み] スマートポインターとは何ですか?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] C++11の'typedef'と'using'の違いは何ですか?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】 unsigned int vs. size_t
-
[解決済み】構造体のベクター初期化について
-
[解決済み】C++でint型に無限大を設定する
-
[解決済み】C++ - 解放されるポインタが割り当てられていないエラー
-
[解決済み] error: 'ostream' does not name a type.
-
[解決済み】抽象クラス型の無効なnew-expression
-
[解決済み】「Expected '(' for function-style cast or type construction」エラーの意味とは?
-
[解決済み】std::cin.getline( ) vs. std::cin
-
[解決済み】システムが指定されたファイルを見つけられませんでした。
-
[解決済み] [Solved] protectedまたはprivateコンストラクタのみを持つクラスで ::std::make_shared を呼び出すにはどうすればよいですか?