私は読んでいた この記事 関数へのスマートポインタの受け渡しについてハーブサッターによって。彼はstd::weak_ptr
について言及しておらず、正直なところ、そのようなスマートポインターを渡すことが役立つ良いシナリオを見つけることができません。
関数は所有権を取得しますか? std::shared_ptr
を渡します。関数は、基になるオブジェクトを操作する必要がありますか?生のポインタまたは参照を渡します。
では、std::weak_ptr
を関数に渡すことは100%役に立たないのでしょうか?
では、
std::weak_ptr
を関数に渡すことは100%役に立たないのでしょうか?
番号。
このおもちゃの例を考えてみましょう。
struct PointerObserver
{
std::weak_ptr<int> held_pointer;
void observe( std::weak_ptr<int> p )
{
held_pointer = std::move(p);
}
void report() const
{
if ( auto sp = held_pointer.lock() )
{
std::cout << "Pointer points to " << *sp << "\n";
}
else
{
std::cout << "Pointer has expired.\n";
}
}
};
この例では、関数observe
が状態を保持します。
そのweak_ptr
パラメーターは、この渡されたポインターが所有していないことを通知しますが、ポインターが期限切れかどうかを安全に検出して、後で所有する機能を予約します。
状態を保持しない後で使用する関数は、関連データが期限切れになる可能性があるマルチスレッドコンテキストでweak_ptr
パラメーターを受け取ることもできます関数が作業を行っている間。
クライアントにweak_ptr
があり、ロジックがそれをロックできるかどうかに関係なく有効である場合は、weak_ptr
を渡します。
具体的な些細な例として:
mutable std::mutex m_mutex;
mutable std::vector<std::weak_ptr<std::function<void(int)>>> m_callbacks;
void clean_callbacks(int x) {
auto l = std::unique_lock<std::mutex>(m_mutex);
auto it = std::remove_if( begin(m_callbacks), end(m_callbacks), [](auto w){ return !w.lock(); } );
m_callbacks.erase( it, end(m_callbacks) );
}
void call_callbacks() {
clean_callbacks();
auto tmp = [&]{
auto l = std::unique_lock<std::mutex>(m_mutex);
return m_callbacks;
}();
for (auto&& wf:tmp) {
if(auto sf = wf.lock()) {
(*sf)(x);
}
}
}
clean_callbacks
には、weak_ptr
をとるラムダがあります。有効期間が終了したm_callbacks
を削除するために使用されます。
このコードは、リスナーが無効になるよりもはるかに頻繁にブロードキャストが発生する単純なブロードキャスターで使用されるため、次のブロードキャストを待って死んだリスナーを排除することは良い戦略です。
弱いポインタは、後で使用できなくなる可能性のあるオブジェクトを保持するのに役立ちます(オブジェクトの有効期間を延長することなく)。これは、通常、コンテナー(または変数)に格納するために使用されることを意味します。通常、オブジェクトが格納されてから弱ポインタに変換されるまで、共有ポインタを渡します。次に、使用する場合、それらがまだ有効であるかどうかを確認するために、最初に共有ポインターに変換する必要があります。したがって、おそらくヘルパー関数で、ストレージおよび取得プロセスの一部を除いて、弱いポインターを渡すことはほとんどありません。
関数の引数としてstd :: shared_ptrの代わりにstd :: weak_ptrを使用することで思いつく唯一の利点は、関数がポインターが有効かどうかをチェックする責任を負い、関数外のチェックを減らすことです。 。
bool function(std::weak_ptr _obj)
{
if(std::shared_ptr obj = _obj.lock())
{
obj->doSomething();
return true;
}
return false
}
代わりに引数としてstd :: shared_ptrを使用する場合、チェックは必要ありませんが、関数を呼び出す前にstd:weak_ptrからstd :: shared_ptrに変換する必要があります。 nullptrが渡されるリスクがある場合でも、それもチェックする必要があります。
void function(std::shared_ptr _obj)
{
_obj->doSomething();
}
したがって、アプリケーションのさまざまな場所からstd :: weak_ptrを関数に何度も渡す場合は、std :: weak_ptrを引数として使用し、関数の外部で毎回チェックするのではなく、その関数でチェックする方がよいと思います。関数。当然、コードの行数は少なくなります。
それ以外の場合、通常はstd :: shared_ptrを渡す場合、関数内でのチェックは当然不要であり、std:weak_ptrが関数内で有効かどうかのチェックはオーバーヘッドになります。