web-dev-qa-db-ja.com

unique_ptrからweak_ptrを構築できないのはなぜですか?

正しく理解していれば、weak_ptrは管理対象オブジェクトの参照カウントを増加させないため、所有権を表しません。オブジェクトにアクセスするだけで、その有効期間は他の誰かが管理します。したがって、weak_ptrunique_ptrから構築できないのはなぜかはわかりませんが、shared_ptrのみです。

誰かがこれを簡単に説明できますか?

48
adam10603

lock()を使用してstd::weak_ptrに変換しない限り、std::shared_ptrは使用できません。標準があなたの提案を許可した場合、それはあなたがそれを使用するためにstd :: weak_ptrを一意に変換する必要があることを意味し、一意性に違反する(またはstd::shared_ptrを再発明する)

説明のために、次の2つのコードを見てください。

std::shared_ptr<int> shared = std::make_shared<int>(10);
std::weak_ptr<int> weak(shared);

{
*(weak.lock()) = 20; //OK, the temporary shared_ptr will be destroyed but the pointee-integer still has shared  to keep it alive
}

あなたの提案で:

std::unique_ptr<int> unique = std::make_unique<int>(10);
std::weak_ptr<int> weak(unique);

{
*(weak.lock()) = 20; //not OK. the temporary unique_ptr will be destroyed but unique still points at it! 
}

そうは言っても、unique_ptrは1つだけで、weak_ptrを間接参照することもできます(別のunique_ptrを作成しなくても)、問題はありません。しかし、1つの参照を持つunique_ptrshared_ptrの違いは何ですか?または、通常のunique_ptrgetを使用して取得するCポインターの違いは何ですか?

weak_ptrは「一般的な非所有リソース」ではなく、非常に具体的な仕事を持っています-weak_ptrの主な目標は、メモリリークを引き起こすshared_ptrの循環ポインティングを防ぐことです。それ以外のことは、プレーンunique_ptrおよびshared_ptrで行う必要があります。

16
David Haim

考えてみると、weak_ptrはオブジェクト自体以外のものを参照する必要があります。これは、オブジェクトが存在しなくなる可能性があり(オブジェクトへの強力なポインタがなくなると)、weak_ptrがオブジェクトがもはや存在しないという情報を含む何かを参照する必要があるためです。

shared_ptrの場合、それは参照カウントを含むものです。しかし、unique_ptrを使用すると、参照カウントが存在しないため、参照カウントを含むものが存在しないため、オブジェクトがなくなっても存在し続けるものはありません。したがって、weak_ptrを参照するものは何もありません。

そのようなweak_ptrを使用する正しい方法もありません。それを使用するには、使用中にオブジェクトが破壊されなかったことを保証する何らかの方法が必要です。 shared_ptrを使用すると簡単です。これがshared_ptrの機能です。しかし、どのようにunique_ptrでそれを行うのですか?明らかにそれらのうちの2つを使用することはできません。他の何かがすでにオブジェクトを所有している必要があります。

32
David Schwartz

shared_ptrには基本的に2つの部分があります。

  1. 指されたオブジェクト
  2. 参照カウントオブジェクト

参照カウントがゼロに下がると、オブジェクト(#1)は削除されます。

ここで、weak_ptrはオブジェクトがまだ存在するかどうかを知る必要があります。これを行うには、参照カウントオブジェクト(#2)がゼロでない場合、参照カウントをインクリメントすることにより、オブジェクトのshared_ptrを作成できる必要があります。カウントがゼロの場合、空のshared_ptrを返します。

次に、参照カウントオブジェクト(#2)をいつ削除できるかを検討しますか? shared_ptr OR weak_ptrオブジェクトが参照するまで待つ必要があります。このため、参照カウントオブジェクトはtwo参照を保持しますcounts、astrongrefおよびaweakref。参照カウントオブジェクトは、両方のカウントがゼロの場合にのみ削除されます。メモリは、すべての弱い参照がなくなった後にのみ解放できます(これは、make_sharedによる 隠れた不利益を意味します )。

tl; dr;weak_ptrは、shared_ptrの一部であるweak reference countに依存し、weak_ptrは存在できませんshared_ptrなし。

12
Motti

概念的には、weak_ptrがアクセスのみを提供し、unique_ptrがライフタイムを制御する実装を妨げるものはありません。ただし、それには問題があります。

  • unique_ptrは、最初に参照カウントを使用しません。弱参照を管理するための管理構造を追加することは可能ですが、追加の動的割り当てが必要です。 unique_ptrは、生のポインターに対する実行時オーバーヘッドを回避することになっているため、そのオーバーヘッドは許容されません。
  • weak_ptrによって参照されるオブジェクトを使用するには、そのオブジェクトから「実際の」参照を抽出する必要があります。これは、最初にポインターの有効期限が切れていないことを検証してから、この実際の参照(shared_ptr この場合)。これは、一意に所有されることになっているオブジェクトへの2番目の参照が突然あることを意味します。これはエラーのレシピです。これは、ポインティッドの破壊を一時的に遅らせるだけの混合ハーフストロングポインタを返すことでは修正できません。ポインティティを保存することもでき、unique_ptrの背後にあるアイデアを無効にすることができます。

ここでweak_ptrを使用して解決しようとしている問題は何ですか?

8
Ulrich Eckhardt

まだ誰も問題のパフォーマンスの側面について言及していないので、$ 0.02を投入させてください。

weak_ptrは、対応するshared_ptrsがすべてスコープ外になり、ポイントされたオブジェクトの割り当てが解除され、破棄されたときを何らかの方法で知る必要があります。これは、shared_ptrsがそれぞれのweak_ptrに対する破壊を何らかの方法で同じオブジェクトに伝える必要があることを意味します。これには一定のコストがかかります。たとえば、weak_ptrがアドレスを取得する(またはオブジェクトが破棄される場合はnullptr)グローバルハッシュテーブルを更新する必要があります。

これには、マルチスレッド環境でのロックも含まれるため、一部のタスクでは遅すぎる可能性があります。

ただし、unique_ptrの目標は、ゼロコスト RAIIスタイルの抽象化クラスを提供することです。したがって、動的に割り当てられたオブジェクトのdeleteing(またはdelete[]ing)以外のコストは発生しません。たとえば、ロックされたハッシュテーブルアクセスまたはガードされたハッシュテーブルアクセスを行うことによって課される遅延は、割り当て解除のコストに匹敵する場合があり、unique_ptrの場合は望ましくありません。

誰もがstd :: weak_ptrについてここに書いているように見えますが、著者が求めているのは弱いポイナーの概念ではないようです

標準ライブラリがunique_ptrにweak_ptrを提供していない理由について誰も言及していないと思います。弱いポインターCONCEPTはunique_ptrの使用を放棄しません。弱いポインターは、オブジェクトが既に削除されている場合にのみ情報であるため、魔法ではなく、非常に単純な一般化されたオブザーバーパターンです。

これは、スレッドセーフとshared_ptrとの一貫性のためです。

あなたは単に、他のスレッドに存在するunique_ptrから作成されたweak_ptrが、ポイントされたオブジェクトのメソッドの呼び出し中に破壊されないことを保証することはできません。これは、weak_ptrが、スレッドセーフを保証するstd :: shared_ptrと一貫している必要があるためです。 unique_ptrで正常に動作するweak_ptrを実装できますが、同じスレッドでのみ使用できます。この場合、ロックメソッドは不要です。 base :: WeakPtrおよびbase :: WeakPtrFactoryのクロムソースを確認できます。unique_ptrで自由に使用できます。 Chromiumの弱いポインタコードはおそらく最後のメンバーの破壊に基づいています-あなたは最後のメンバーとしてファクトリを追加する必要があり、その後WeakPtrはオブジェクトの削除に通知されていると信じています(私は100%確信していません)-それは見えません実装が難しい。

全体的に、弱いポインターの概念でunique_ptrを使用することは問題ありません。

3
Bartosz Piękny

unique_ptrよりもshared_ptrを好む理由を区別することが役立つ場合があります。

パフォーマンス明らかな理由の1つは、計算時間とメモリの使用です。現在定義されているように、shared_ptrオブジェクトには通常、参照カウント値のようなものが必要です。 unique_ptrオブジェクトはサポートしていません。

セマンティックスunique_ptrを使用すると、プログラマは、指示されたオブジェクトが破棄されるとき、unique_ptrが破棄されるとき、またはその変更メソッドが呼び出されます。そのため、大規模または馴染みのないコードベースでは、unique_ptrを使用すると、プログラムの実行時動作に関する明らかでない情報を静的に伝達(および強制)します。

上記のコメントは一般に、weak_ptrオブジェクトがunique_ptrオブジェクトに結び付けられることは望ましくないというパフォーマンスベースの理由に焦点を当てています。しかし、セマンティクスベースの引数が、STLの将来のバージョンが元の質問で暗示されているユースケースをサポートするのに十分な理由であるかどうか疑問に思うかもしれません。

0