std :: weak_ptrはいつ役立つのですか?


回答:


231

良い例はキャッシュです。

最近アクセスしたオブジェクトについては、それらをメモリに保持したいので、それらへの強力なポインタを保持します。定期的に、キャッシュをスキャンして、最近アクセスされていないオブジェクトを決定します。それらをメモリに保持する必要はないので、強いポインタを取り除くことができます。

しかし、そのオブジェクトが使用中で、他のコードがそのオブジェクトへの強力なポインタを保持している場合はどうでしょうか。キャッシュがオブジェクトへの唯一のポインターを取り除くと、それを再び見つけることはできません。そのため、キャッシュは、オブジェクトがメモリに残っている場合に見つける必要があるオブジェクトへの弱いポインタを保持します。

これは、ウィークポインタが行うこととまったく同じです。オブジェクトがまだ存在している場合は、オブジェクトを見つけることができますが、他に何も必要がない場合は、オブジェクトを保持しません。


8
std :: wake_ptrは別のポインターが指す場所のみを指すことができ、指すオブジェクトが削除されたとき/他のポインターによって指し示されていないときにnullptrを指すのですか?

27
@RM:基本的にはい。ポインタが弱い場合、それを強いポインタに昇格させることができます。そのオブジェクトがまだ存在している場合(少なくとも1つの強力なポインターがまだ存在しているため)、その操作は成功し、強力なポインターが与えられます。そのオブジェクトが存在しない場合(すべての強いポインターが消えたため)、その操作は失敗します(通常、弱いポインターを捨てることで反応します)。
David Schwartz

12
強いポインタはオブジェクトを存続させますが、weak_ptrはそれを見ることができます...オブジェクトの寿命にこだわることなく。
Vivandiere 2016

3
私が少なくとも数回使用したもう1つの例は、オブザーバーを実装するときに、サブジェクトにウィークポインターのリストを維持させ、独自のリストクリーンアップを実行させると便利な場合があります。オブザーバーが削除されたときにオブザーバーを明示的に削除する労力を少し節約できます。さらに重要なことに、オブザーバーを破棄するときに利用可能なサブジェクトに関する情報を用意する必要がないため、通常は非常に簡単になります。
Jason C

3
ちょっと待ってください。shared_ptrを保持するキャッシュの何が問題になっていて、メモリからクリアする必要があるときにリストから削除するだけですか?すべてのユーザーがshared_ptrを同じように保持し、キャッシュされたリソースは、すべてのユーザーがそれを使い終えるとすぐにクリアされます。
rubenvb

299

std::weak_ptrぶら下がりポインタの問題を解決する非常に良い方法です。生のポインタを使用するだけでは、参照されたデータが割り当て解除されたかどうかを知ることは不可能です。代わりに、std::shared_ptrデータを管理させ、データのstd::weak_ptrユーザーに提供することにより、ユーザーは、expired()またはを呼び出してデータの有効性を確認できます。lock()ます。

すべてのインスタンスが削除される前に削除されないデータの所有権をstd::shared_ptrすべてのstd::shared_ptrインスタンスが共有するため、これを単独で行うことはできませんstd::shared_ptr。以下は、ダングリングポインターを確認する方法の例ですlock()

#include <iostream>
#include <memory>

int main()
{
    // OLD, problem with dangling pointer
    // PROBLEM: ref will point to undefined data!

    int* ptr = new int(10);
    int* ref = ptr;
    delete ptr;

    // NEW
    // SOLUTION: check expired() or lock() to determine if pointer is valid

    // empty definition
    std::shared_ptr<int> sptr;

    // takes ownership of pointer
    sptr.reset(new int);
    *sptr = 10;

    // get pointer to data without taking ownership
    std::weak_ptr<int> weak1 = sptr;

    // deletes managed object, acquires new pointer
    sptr.reset(new int);
    *sptr = 5;

    // get pointer to new data without taking ownership
    std::weak_ptr<int> weak2 = sptr;

    // weak1 is expired!
    if(auto tmp = weak1.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak1 is expired\n";

    // weak2 points to new data (5)
    if(auto tmp = weak2.lock())
        std::cout << *tmp << '\n';
    else
        std::cout << "weak2 is expired\n";
}

1
OK、ローカルに(所有)ポインタをnull(メモリを削除)に設定した場合と同じように、同じメモリへの他のすべての(弱い)ポインタもnullに設定されます
Pat-Laugh

std::weak_ptr::lockstd::shared_ptr管理オブジェクトの所有権を共有する新しいを作成します。
Sahib Yar

129

別の答え、うまくいけばもっと簡単に。(仲間のグーグルのために)

とオブジェクトがあるTeamとしMemberます。

明らかにそれは関係です:Teamオブジェクトはそのへのポインタを持ちMembersます。また、メンバーにはTeamオブジェクトへのバックポインターもある可能性があります。

次に、依存サイクルがあります。を使用shared_ptrすると、循環参照でお互いを参照するため、参照を放棄したときにオブジェクトが自動的に解放されなくなります。これはメモリリークです。

これは、を使用して解除しweak_ptrます。「所有者」は通常使用shared_ptrし、「所有者」はweak_ptrその親を使用し、その親へのアクセスが必要なときに一時的に変換しshared_ptrます。

弱いptrを保存します:

weak_ptr<Parent> parentWeakPtr_ = parentSharedPtr; // automatic conversion to weak from shared

その後、必要に応じて使用します

shared_ptr<Parent> tempParentSharedPtr = parentWeakPtr_.lock(); // on the stack, from the weak ptr
if( !tempParentSharedPtr ) {
  // yes, it may fail if the parent was freed since we stored weak_ptr
} else {
  // do stuff
}
// tempParentSharedPtr is released when it goes out of scope

1
これはどのようにメモリリークですか?チームが破壊されると、メンバーが破壊されるため、shared_ptr参照カウントは0になり、破壊されますか?
ポール、2014年

4
@paulmチームは「その」メンバーを破棄しません。重要なのshared_ptrは所有権を共有することです。そのため、メモリを解放する特別な責任は誰にもありません。使用されなくなったときに自動的に解放されます。ループがない限り...複数のチームが同じプレーヤーを共有している場合があります(過去のチーム?)。チームオブジェクトがメンバーを「所有」している場合はshared_ptr、最初にを使用する必要はありません。
Offirmo 2014年

1
それらを破壊することはありませんが、そのshared_ptrはスコープから外れ、use_countをデクリメントします。したがって、この時点でuse_countは0なので、shared_ptrはそのポイントを削除しますか?
ポール、2014年

2
@paulmあなたは正しいです。しかし、この例では、チームはshared_ptrその「チームメンバー」から参照されているため、いつ破壊されますか?あなたが説明しているのはループがない場合です。
Offirmo 2014年

14
悪くないと思います。メンバーが多くのチームに所属できる場合、参照を使用しても機能しません。
Mazyod

22

@jleahyによって私に与えられた1つの例を次に示します。タスクのコレクションがあり、非同期で実行され、によって管理されているとしstd::shared_ptr<Task>ます。これらのタスクで定期的に何かを実行したい場合があるので、タイマーイベントがa std::vector<std::weak_ptr<Task>>をトラバースし、タスクに実行する何かを与えることができます。ただし、同時に、タスクが不要になり、終了したとタスクが同時に判断した可能性があります。したがって、タイマーは、弱いポインターから共有ポインターを作成し、その共有ポインターを使用することにより、タスクがまだ生きているかどうかを確認できます(nullでない場合)。


4
:良い例のように聞こえますが、もう少し詳しく例を挙げていただけますか?タスクが終了すると、定期的なチェックなしでstd :: vector <std :: weak_ptr <Task >>から既に削除されているはずです。したがって、ここでstd :: vector <std :: weak_ptr <>>が非常に役立つかどうかはわかりません。
Gob00st

キューに関する同様のコメント:オブジェクトがあり、それらをいくつかのリソースのキューに入れると、待機中にオブジェクトが削除される可能性があります。そのため、weak_ptrsをキューに入れる場合、そこからエントリを削除することに煩わされる必要はありません。Weak_ptrsは無効化され、発生すると破棄されます。
zzz777 2014年

1
@ zzz777:オブジェクトを無効にするロジックは、キューまたはオブザーバーのベクトルの存在を認識していない場合もあります。したがって、オブザーバーは弱いポインターに対して個別のループを実行し、まだ生きているポインターに作用し、コンテナーから死んだポインターを削除します...
Kerrek SB

1
@KerekSB:はい。キューの場合、個別のループを実行する必要すらありません。有効なリソース(存在する場合)が得られるまで、リソースを使用して、期限切れのweak_ptrs(存在する場合)を破棄します。
zzz777 14年

スレッドにコレクションからスレッド自体を削除させることもできますが、これにより依存関係が作成され、ロックが必要になります。
curiousguy

16

これらは、非同期ハンドラーが呼び出されたときにターゲットオブジェクトがまだ存在することが保証されていない場合に、Boost.Asioで役立ちます。トリックはweak_ptrstd::bindまたはラムダキャプチャを使用して、非同期ハンドラオブジェクトにをバインドすることです。

void MyClass::startTimer()
{
    std::weak_ptr<MyClass> weak = shared_from_this();
    timer_.async_wait( [weak](const boost::system::error_code& ec)
    {
        auto self = weak.lock();
        if (self)
        {
            self->handleTimeout();
        }
        else
        {
            std::cout << "Target object no longer exists!\n";
        }
    } );
}

これはの変形であるself = shared_from_this()保留中の非同期ハンドラがしますしばしばBoost.Asioの例で見られるイディオム、ないターゲットオブジェクトの寿命を延ばす、まだターゲットオブジェクトが削除された場合、まだ安全です。


なぜこの回答を見つけるのにそれほど時間がかかったのですか...あなたのキャプチャを使用していないPSthis
Orwellophile

@Orwellophileが修正されました。self = shared_from_this()ハンドラーが同じクラス内のメソッドを呼び出すときにイディオムを使用するときの習慣の力。
Emile Cormier

16

shared_ptr:実際のオブジェクトを保持します。

weak_ptrlock実際の所有者に接続するために使用するか、shared_ptrそうでなければNULLを返します。

弱いポイント

大まかに言えば、weak_ptr役割は住宅代行の役割に似ています。エージェントがいない場合、家を借りるには、市内のランダムな家をチェックする必要があります。エージェントは、アクセス可能で賃貸可能な家だけを訪問するようにします


14

weak_ptrオブジェクトの正しい削除を確認することもできます-特に単体テストで。一般的な使用例は次のようになります。

std::weak_ptr<X> weak_x{ shared_x };
shared_x.reset();
BOOST_CHECK(weak_x.lock());
... //do something that should remove all other copies of shared_x and hence destroy x
BOOST_CHECK(!weak_x.lock());

13

ポインターを使用するときは、使用可能なポインターのさまざまなタイプを理解し、それぞれを使用することが理にかなっていることが重要です。次の2つのカテゴリには、4種類のポインタがあります。

  • 生のポインタ:
    • 生のポインタ[ie SomeClass* ptrToSomeClass = new SomeClass();]
  • スマートポインター:
    • ユニークポインタ[ie
      std::unique_ptr<SomeClass> uniquePtrToSomeClass ( new SomeClass() );
      ]
    • 共有ポインタ[すなわち
      std::shared_ptr<SomeClass> sharedPtrToSomeClass ( new SomeClass() );
      ]
    • 弱いポインタ[すなわち
      std::weak_ptr<SomeClass> weakPtrToSomeWeakOrSharedPtr ( weakOrSharedPtr );
      ]

生のポインタ(「レガシーポインタ」または「Cポインタ」と呼ばれることもあります)は「ベアボーン」ポインタの動作を提供し、バグとメモリリークの一般的な原因です。生のポインタはリソースの所有権を追跡する手段を提供しないため、開発者はメモリリークが発生していないことを確認するために手動で「削除」を呼び出す必要があります。リソースが共有されている場合、オブジェクトがまだリソースを指しているかどうかを知るのは難しいため、これは困難になります。これらの理由により、生のポインターは一般に避け、範囲が限定されたコードのパフォーマンスが重要なセクションでのみ使用する必要があります。

一意のポインタは、リソースへの基になる生のポインタを「所有」する基本的なスマートポインタであり、一意のポインタを「所有」するオブジェクトがスコープ外になると、deleteを呼び出して割り当てられたメモリを解放します。「一意」という名前は、1つのオブジェクトのみが特定の時点で一意のポインタを「所有」できるという事実を指します。所有権はmoveコマンドを介して別のオブジェクトに転送できますが、一意のポインターをコピーまたは共有することはできません。これらの理由により、特定の時点で1つのオブジェクトのみがポインターを必要とする場合、一意のポインターはrawポインターの良い代替手段となります。これにより、所有者のオブジェクトのライフサイクルの最後にメモリを解放する必要がなくなります。

共有ポインターは、一意のポインターに似た別のタイプのスマートポインターですが、多くのオブジェクトが共有ポインターに対する所有権を持つことができます。一意のポインタと同様に、共有ポインタは、すべてのオブジェクトがリソースを指していると、割り当てられたメモリを解放します。これは、参照カウントと呼ばれる手法でこれを実現します。新しいオブジェクトが共有ポインタの所有権を取得するたびに、参照カウントが1ずつ増加します。同様に、オブジェクトがスコープから外れたり、リソースへの参照を停止したりすると、参照カウントが1つ減ります。参照カウントがゼロに達すると、割り当てられたメモリが解放されます。これらの理由により、共有ポインターは非常に強力なタイプのスマートポインターであり、複数のオブジェクトが同じリソースを指す必要がある場合に使用する必要があります。

最後に、弱いポインターは、リソースを直接指すのではなく、別のポインター(弱いまたは共有)を指す別のタイプのスマートポインターです。ウィークポインターはオブジェクトに直接アクセスできませんが、オブジェクトがまだ存在するかどうか、またはオブジェクトが期限切れかどうかを知ることができます。弱いポインターを一時的に共有ポインターに変換して、指し示されたオブジェクトにアクセスできます(オブジェクトがまだ存在する場合)。説明のために、次の例を検討してください。

  • あなたは忙しく、会議が重複しています:会議Aと会議B
  • あなたはミーティングAに行くことに決め、同僚はミーティングBに行く
  • 同僚に、ミーティングAの終了後もミーティングBがまだ続く場合は、参加することを伝えます
  • 次の2つのシナリオが実行される可能性があります。
    • 会議Aが終了し、会議Bがまだ進行中なので、参加します
    • 会議Aが終了し、会議Bも終了したため、参加できません

この例では、会議Bへのポインタが弱いです。あなたは会議Bの「所有者」ではないため、会議なしで終了する可能性があり、確認しない限り、それが終了したかどうかわかりません。終了していない場合は、参加して参加できます。それ以外の場合は参加できません。これは、ミーティングAとミーティングBの両方の「所有者」になる(同時に両方に参加する)ため、ミーティングBへの共有ポインタを持つこととは異なります。

この例は、弱いポインターがどのように機能するかを示しており、オブジェクトが外部オブザーバーである必要があるが、所有権を共有する責任を望まない場合に役立ちます。これは、2つのオブジェクトがお互いを指す必要があるシナリオ(別名循環参照)で特に役立ちます。共有ポインターでは、他のオブジェクトから「強く」ポイントされているため、どちらのオブジェクトも解放できません。ポインターの1つがウィークポインターである場合、ウィークポインターを保持しているオブジェクトは、まだ存在していれば、必要に応じて他のオブジェクトにアクセスできます。


6

すでに述べた他の有効なユースケースstd::weak_ptrとは別に、マルチスレッド環境での素晴らしいツールです。

  • オブジェクトを所有していないため、別のスレッドでの削除を妨げることはできません
  • std::shared_ptrと組み合わせて、std::weak_ptrダングリングポインターに対して安全です- std::unique_ptr生のポインターと組み合わせて反対
  • std::weak_ptr::lock()アトミック操作です(weak_ptrのスレッドセーフについても参照)

ディレクトリ(〜10.000)のすべての画像を同時にメモリに(たとえば、サムネイルキャッシュとして)ロードするタスクを考えてみます。明らかに、これを行うための最良の方法は、イメージを処理および管理する制御スレッドと、イメージをロードする複数のワーカースレッドです。これは簡単な作業です。これは非常に単純化された実装です(join()etcは省略され、実際の実装ではスレッドは異なる方法で処理される必要があります)。

// a simplified class to hold the thumbnail and data
struct ImageData {
  std::string path;
  std::unique_ptr<YourFavoriteImageLibData> image;
};

// a simplified reader fn
void read( std::vector<std::shared_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageData : imagesToLoad )
     imageData->image = YourFavoriteImageLib::load( imageData->path );
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::shared_ptr<ImageData>>> splitDatas = 
        splitImageDatas( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

しかし、たとえばユーザーが別のディレクトリを選択したために、イメージのロードを中断したい場合は、さらに複雑になります。または、マネージャーを破壊したい場合でも。

m_imageDatasフィールドを変更する前に、スレッド通信が必要で、すべてのローダースレッドを停止する必要があります。それ以外の場合、ローダーは、古いイメージであっても、すべてのイメージが完了するまでロードを続けます。単純化された例では、それほど難しくはありませんが、実際の環境では、状況ははるかに複雑になる可能性があります。

スレッドはおそらく複数のマネージャーによって使用されるスレッドプールの一部であり、そのうちのいくつかは停止されており、一部は停止されていません。単純なパラメーターimagesToLoadはロックされたキューであり、これらのマネージャーは、さまざまな制御スレッドからイメージ要求をプッシュします。読者が反対側でリクエストをポップします-任意の順序で。そのため、通信が困難になり、遅くなり、エラーが発生しやすくなります。このような場合に追加の通信を回避する非常にエレガントな方法はstd::shared_ptr、と組み合わせて使用することstd::weak_ptrです。

// a simplified reader fn
void read( std::vector<std::weak_ptr<ImageData>> imagesToLoad ) {
   for( auto& imageDataWeak : imagesToLoad ) {
     std::shared_ptr<ImageData> imageData = imageDataWeak.lock();
     if( !imageData )
        continue;
     imageData->image = YourFavoriteImageLib::load( imageData->path );
   }
}

// a simplified manager
class Manager {
   std::vector<std::shared_ptr<ImageData>> m_imageDatas;
   std::vector<std::unique_ptr<std::thread>> m_threads;
public:
   void load( const std::string& folderPath ) {
      std::vector<std::string> imagePaths = readFolder( folderPath );
      m_imageDatas = createImageDatas( imagePaths );
      const unsigned numThreads = std::thread::hardware_concurrency();
      std::vector<std::vector<std::weak_ptr<ImageData>>> splitDatas = 
        splitImageDatasToWeak( m_imageDatas, numThreads );
      for( auto& dataRangeToLoad : splitDatas )
        m_threads.push_back( std::make_unique<std::thread>(read, dataRangeToLoad) );
   }
};

この実装は、最初の実装とほぼ同じくらい簡単で、追加のスレッド通信を必要とせず、実際の実装ではスレッドプール/キューの一部になる可能性があります。期限切れのイメージはスキップされ、期限切れでないイメージが処理されるため、通常の操作中にスレッドを停止する必要はありません。所有者のポインターが期限切れになっていないかどうかリーダーのfnがチェックするため、パスをいつでも安全に変更したり、マネージャーを破棄したりできます。


2

http://en.cppreference.com/w/cpp/memory/weak_ptr std :: weak_ptrは、std :: shared_ptrによって管理されるオブジェクトへの非所有(「弱い」)参照を保持するスマートポインターです。参照されるオブジェクトにアクセスするには、std :: shared_ptrに変換する必要があります。

std :: weak_ptrは一時的な所有権をモデル化します。オブジェクトが存在する場合にのみアクセスする必要があり、他のユーザーがいつでも削除できる場合、std :: weak_ptrを使用してオブジェクトを追跡し、stdに変換します。 :shared_ptrは一時的な所有権を想定します。この時点で元のstd :: shared_ptrが破棄されると、オブジェクトのライフタイムは、一時的なstd :: shared_ptrも破棄されるまで延長されます。

さらに、std :: weak_ptrは、std :: shared_ptrの循環参照を解除するために使用されます。


循環参照を解除するには
curiousguy

2

共有ポインターの欠点があります:shared_pointerは親子サイクルの依存関係を処理できません。子クラスが親クラスのオブジェクトを使用する場合、同じファイルで、親クラスが共有ポインターを使用して子クラスのオブジェクトを使用するかどうかを意味します。共有ポインターがサイクル依存関係のシナリオでデストラクターを呼び出していない場合でも、共有ポインターはすべてのオブジェクトを破棄できません。基本的に共有ポインタは、参照カウントメカニズムをサポートしていません。

この欠点は、weak_pointerを使用して克服できます。


弱い参照は循環依存にどのように対処できますか?
curiousguy

1
@curiousguy、子は親への弱い参照を使用し、それを指している共有(強い)参照がない場合、親は割り当て解除できます。したがって、子を介して親にアクセスする場合、弱い参照は親がまだ利用可能かどうかを確認するためにテストする必要があります。または、その余分な条件を回避するために、循環参照トレースメカニズム(マークスイープまたは参照カウントデクリメントのプローブ、どちらも漸近パフォーマンスが悪い)は、親と子への唯一の共有参照がそれぞれからのものである場合に、循環共有参照を壊すことができます。その他。
シェルビームーアIII

@ShelbyMooreIII " は、親がまだ利用可能かどうかを確認するためにテストする必要があります "はい、利用できない場合に正しく対応できる必要があります!これは実際の(つまり強い)参照では発生しません。つまり、弱い参照は置換のドロップではありません。つまり、ロジックの変更が必要です。
curiousguy

2
@curiousguyあなたは尋ねませんでした「weak_ptrプログラムロジックを変更せずに循環依存関係をドロップイン置換の方法で対処するにはどうすればよいshared_ptrですか?」:-)
シェルビームーアIII

2

オブジェクトを所有したくない場合:

例:

class A
{
    shared_ptr<int> sPtr1;
    weak_ptr<int> wPtr1;
}

上記のクラスでは、wPtr1はwPtr1が指すリソースを所有していません。リソースが削除された場合、wPtr1は期限切れになります。

循環依存を回避するには:

shard_ptr<A> <----| shared_ptr<B> <------
    ^             |          ^          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
    |             |          |          |
class A           |     class B         |
    |             |          |          |
    |             ------------          |
    |                                   |
    -------------------------------------

クラスBとAのshared_ptrを作成すると、両方のポインターのuse_countは2になります。

shared_ptrがスコープ外に出てもカウントは1のままなので、AおよびBオブジェクトは削除されません。

class B;

class A
{
    shared_ptr<B> sP1; // use weak_ptr instead to avoid CD

public:
    A() {  cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; }

    void setShared(shared_ptr<B>& p)
    {
        sP1 = p;
    }
};

class B
{
    shared_ptr<A> sP1;

public:
    B() {  cout << "B()" << endl; }
    ~B() { cout << "~B()" << endl; }

    void setShared(shared_ptr<A>& p)
    {
        sP1 = p;
    }
};

int main()
{
    shared_ptr<A> aPtr(new A);
    shared_ptr<B> bPtr(new B);

    aPtr->setShared(bPtr);
    bPtr->setShared(aPtr);

    return 0;  
}

出力:

A()
B()

出力からわかるように、AおよびBポインターは削除されないため、メモリリークが発生します。

このような問題を回避するには、クラスAでshared_ptrの代わりにweak_ptrを使用してください。


2

私はへstd::weak_ptr<T>ハンドルstd::shared_ptr<T>と見なします。それによりstd::shared_ptr<T>、それがまだ存在する場合にを取得できますが、その寿命は延長されません。このような視点が役立つシナリオはいくつかあります。

// Some sort of image; very expensive to create.
std::shared_ptr< Texture > texture;

// A Widget should be able to quickly get a handle to a Texture. On the
// other hand, I don't want to keep Textures around just because a widget
// may need it.

struct Widget {
    std::weak_ptr< Texture > texture_handle;
    void render() {
        if (auto texture = texture_handle.get(); texture) {
            // do stuff with texture. Warning: `texture`
            // is now extending the lifetime because it
            // is a std::shared_ptr< Texture >.
        } else {
            // gracefully degrade; there's no texture.
        }
    }
};

別の重要なシナリオは、データ構造のサイクルを壊すことです。

// Asking for trouble because a node owns the next node, and the next node owns
// the previous node: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > next;
    std::shared_ptr< Node > prev;
};

// Asking for trouble because a parent owns its children and children own their
// parents: memory leak; no destructors automatically called.
struct Node {
    std::shared_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::shared_ptr< Node > next;
    std::weak_ptr< Node > prev;
};

// Better: break dependencies using a std::weak_ptr (but not best way to do it;
// see Herb Sutter's talk).
struct Node {
    std::weak_ptr< Node > parent;
    std::shared_ptr< Node > left_child;
    std::shared_ptr< Node > right_child;
};

Herb Sutterは、言語機能(この場合はスマートポインター)の最適な使用法を説明する優れた講演を行っており、デフォルトで漏れのないことを保証し ます(つまり、すべてが構造によって所定の位置にカチッとはまるため、ほとんどねじ込むことができません)。必見です。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.