私はC++11のスマートポインタの研究を始めましたが、の有用な使用法は見当たりませんstd::weak_ptr
。std::weak_ptr
誰かがいつ役に立つ/必要か教えてもらえますか?
14 に答える
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";
}
出力
weak1 is expired
5
良い例はキャッシュです。
最近アクセスしたオブジェクトの場合、それらをメモリに保持する必要があるため、それらへの強力なポインタを保持します。定期的にキャッシュをスキャンして、最近アクセスされていないオブジェクトを特定します。それらをメモリに保持する必要がないので、強力なポインタを取り除きます。
しかし、そのオブジェクトが使用中であり、他のコードがそのオブジェクトへの強力なポインターを保持している場合はどうなるでしょうか。キャッシュがオブジェクトへの唯一のポインタを取り除くと、キャッシュは二度と見つけることができなくなります。したがって、キャッシュは、オブジェクトがメモリに残っている場合に見つける必要のあるオブジェクトへの弱いポインタを保持します。
これはまさに弱いポインタが行うことです。オブジェクトがまだ存在している場合はオブジェクトを見つけることができますが、他に必要なものがない場合はオブジェクトを保持しません。
別の答え、うまくいけばもっと簡単です。(仲間のグーグルのために)
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
@jleahyによって私に与えられた1つの例を次に示します。タスクのコレクションがあり、非同期で実行され、によって管理されているとしstd::shared_ptr<Task>
ます。これらのタスクで定期的に何かをしたい場合があるので、タイマーイベントがaをトラバースしstd::vector<std::weak_ptr<Task>>
て、タスクに何かを与えることができます。ただし、同時に、タスクは、それがもはや必要でなく、死ぬことを同時に決定した可能性があります。したがって、タイマーは、弱いポインターから共有ポインターを作成し、その共有ポインターを使用することで、タスクがまだ生きているかどうかを確認できます(nullでない場合)。
これらは、非同期ハンドラーが呼び出されたときにターゲット オブジェクトがまだ存在することが保証されていない場合に、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 の例でよく見られるイディオムの変形であり、保留中の非同期ハンドラーはターゲット オブジェクトの有効期間を延長しませんが、ターゲット オブジェクトが削除されても安全です。
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());
へのハンドル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 は、言語機能 (この場合はスマート ポインター) の最適な使用法を説明する素晴らしい講演を行っており、デフォルトでリーク フリーダムを保証 します (つまり、すべてが構築によって所定の位置に収まります。ほとんど失敗することはありません)。必見です。
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 が使用されます。