315

私はC++11のスマートポインタの研究を始めましたが、の有用な使用法は見当たりませんstd::weak_ptrstd::weak_ptr誰かがいつ役に立つ/必要か教えてもらえますか?

4

14 に答える 14

337

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

のすべてのインスタンスが削除される前に削除されないデータの所有権をすべてのインスタンスが共有するstd::shared_ptrため、これを単独で行うことはできません。を使用してダングリング ポインターをチェックする方法の例を次に示します。std::shared_ptrstd::shared_ptrlock()

#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";
}

出力

weak1 is expired
5
于 2014-02-19T10:03:01.263 に答える
265

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

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

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

これはまさに弱いポインタが行うことです。オブジェクトがまだ存在している場合はオブジェクトを見つけることができますが、他に必要なものがない場合はオブジェクトを保持しません。

于 2012-08-19T23:06:28.020 に答える
163

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

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

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

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

を使用してこれを破りますweak_ptr。「所有者」は通常使用shared_ptrし、「所有」は親に a を使用し、親にアクセスする必要があるときに一時的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
于 2013-07-03T10:46:56.117 に答える
24

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

于 2012-08-19T23:07:41.513 に答える
20

これらは、非同期ハンドラーが呼び出されたときにターゲット オブジェクトがまだ存在することが保証されていない場合に、Boost.Asio で役立ちます。秘訣は、またはラムダ キャプチャweak_ptrを使用して、 a を非同期ハンドラー オブジェクトにバインドすることです。std::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 の例でよく見られるイディオムの変形であり、保留中の非同期ハンドラーはターゲット オブジェクトの有効期間を延長しませが、ターゲット オブジェクトが削除されても安全です。

于 2015-12-16T02:12:22.370 に答える
19

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

weak_ptr :lock実際の所有者に接続するために使用するか、それ以外の場合は NULL を返しますshared_ptr

弱いptr

大雑把に言えば、weak_ptr役割は住宅仲介業者の役割に似ています。エージェントがいなければ、家を借りるには、街中の家をランダムにチェックする必要があるかもしれません。エージェントは、まだアクセス可能で賃貸可能な家だけを訪問するようにします。

于 2016-10-21T14:02:14.770 に答える
17

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());
于 2014-06-05T12:55:53.660 に答える
4

へのハンドル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.
        }
    }
};

もう 1 つの重要なシナリオは、データ構造の循環を断ち切ることです。

// 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 は、言語機能 (この場合はスマート ポインター) の最適な使用法を説明する素晴らしい講演を行っており、デフォルトでリーク フリーダムを保証 します (つまり、すべてが構築によって所定の位置に収まります。ほとんど失敗することはありません)。必見です。

于 2018-10-28T00:04:27.213 に答える
3

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::shared_ptr の循環参照を解除するために std::weak_ptr が使用されます。

于 2014-05-12T11:57:47.453 に答える