将std::weak_ptr传递给函数是否有用?

13

我正在阅读Herb Sutter关于向函数传递智能指针的文章,链接如下:https://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/。他没有提到std::weak_ptr,而且我无法找到一个好的场景来使用这种智能指针。

如果函数需要所有权,请传递std::shared_ptr。如果函数只需要操作底层对象,请传递原始指针或引用。

那么,向函数传递std::weak_ptr是否百分之百没有用处呢?


对于函数,是的,或许可以,但对于方法,那就很有意思了。 - Matthieu Brucher
5
这要看具体情况。std::weak_ptr 表示你并不拥有这个对象,当你试图获取共享所有权时,它可能已经不存在了。如果这正是你想要的,那么就应该使用它。 - NathanOliver
4个回答

19

传递std::weak_ptr到函数中完全没有用吗?

不是。

考虑这个玩具例子。

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。这通常被称为“sink parameter”(接受参数)。
这个 weak_ptr 参数表明这个被传递的指针不是拥有者,但保留以后可能拥有的能力,安全地检测指针是否已经过期。
作为另一个例子,在多线程上下文中,一个不持有状态以供以后使用的函数也可以有用地接收 weak_ptr 参数,因为相关数据可能在该函数执行期间过期

2
这个答案被isocpp.org官方认可,太棒了! - Dan

3
如果您的客户端有一个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 的 lambda 表达式,用于删除任何生命周期已结束的 m_callbacks

这段代码用于简单广播器中,广播发生的频率要远远高于监听器失效的情况,所以等待下一次广播以消除死亡的监听器是一个好策略。


2

弱指针对于持有可能在后续不可用的对象(而不延长其生命周期)非常有用。这意味着它们通常用于存储在容器(或变量)中。通常会传递一个共享指针,直到对象被存储,然后转换为弱指针。然后,在使用时必须先将其转换为共享指针,以检查它们是否仍然有效。因此,除了存储和检索过程中的辅助函数之外,很少会传递弱指针。


2

在函数中使用std::weak_ptr而不是std::shared_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是否有效将会增加开销。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接