我开始研究c++ 11的智能指针,我没有看到std::weak_ptr有任何有用的用途。有人能告诉我什么时候std::weak_ptr是有用的/必要的吗?


当前回答

另一个答案,希望更简单。(对谷歌员工)

假设您有Team和Member对象。

显然,这是一个关系:Team对象将拥有指向其成员的指针。成员也可能有一个指向他们的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

其他回答

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的循环引用。

共享指针有一个缺点: Shared_pointer不能处理父子周期依赖关系。如果父类使用父类的对象使用共享指针,则表示在同一文件中,如果子类使用父类的对象。共享指针将无法析构所有对象,甚至在循环依赖场景中共享指针根本不调用析构函数。基本上共享指针不支持引用计数机制。

我们可以使用weak_pointer来克服这个缺点。

它们在Boost中很有用。当调用异步处理程序时,不能保证目标对象仍然存在。诀窍是使用std::bind或lambda capture将weak_ptr绑定到异步处理程序对象中。

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

这是在Boost中经常看到的self = shared_from_this()习惯用法的变体。Asio示例,其中挂起的异步处理程序不会延长目标对象的生命周期,但如果目标对象被删除,则仍然是安全的。

我看到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;
};

赫布·萨特有一篇精彩的演讲,解释了语言的最佳使用 特性(在这种情况下是智能指针),以确保默认情况下的泄漏自由 (意思是:所有的东西都是通过建设来实现的;你很难搞砸它 )。这是必看的节目。

Shared_ptr:保存真实对象。

weak_ptr:使用lock连接到真正的所有者,否则返回NULL shared_ptr。

大致来说,weak_ptr角色类似于房屋中介的角色。如果没有中介,要想租到房子,我们可能得在城里随机找房子。中介会确保我们只去那些还能租到的房子。