与临时所有权共享(stdweak ptr)

std::weak_ptr实例可以指向由 std::shared_ptr实例拥有的对象,而只是自己成为临时所有者。这意味着弱指针不会改变对象的引用计数,因此如果重新分配或销毁所有对象的共享指针,则不会阻止对象的删除。

在下面的示例中,使用了 std::weak_ptr 的实例,以便不会禁止破坏树对象:

#include <memory>
#include <vector>

struct TreeNode {
    std::weak_ptr<TreeNode> parent;
    std::vector< std::shared_ptr<TreeNode> > children;
};

int main() {
    // Create a TreeNode to serve as the root/parent.
    std::shared_ptr<TreeNode> root(new TreeNode);

    // Give the parent 100 child nodes.
    for (size_t i = 0; i < 100; ++i) {
        std::shared_ptr<TreeNode> child(new TreeNode);
        root->children.push_back(child);
        child->parent = root;
    }

    // Reset the root shared pointer, destroying the root object, and
    // subsequently its child nodes.
    root.reset();
}

当子节点被添加到根节点的子节点时,他们的 std::weak_ptr 成员 parent 被设置为根节点。成员 parent 被声明为弱指针而不是共享指针,因此根节点的引用计数不会递增。当根节点在 main() 结束时重置时,根被破坏。由于对子节点的唯一剩余的 std::shared_ptr 引用包含在根的集合 children 中,所以所有子节点随后也被销毁。

由于控制块实现细节,在 shared_ptr 参考计数器和 weak_ptr 参考计数器都达到零之前,可能不会释放 shared_ptr 分配的内存。

#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         {
             // std::make_shared is optimized by allocating only once 
             // while std::shared_ptr<int>(new int(42)) allocates twice.
             // Drawback of std::make_shared is that control block is tied to our integer
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // sh memory should be released at this point...
         }
         // ... but wk is still alive and needs access to control block
     }
     // now memory is released (sh and wk)
}

由于 std::weak_ptr 不会使其引用的对象保持活动状态,因此无法通过 std::weak_ptr 直接访问数据。相反,它提供了一个 lock() 成员函数,它试图将 std::shared_ptr 检索到引用的对象:

#include <cassert>
#include <memory>
int main()
{
    {
         std::weak_ptr<int> wk;
         std::shared_ptr<int> sp;
         {
             std::shared_ptr<int> sh = std::make_shared<int>(42);
             wk = sh;
             // calling lock will create a shared_ptr to the object referenced by wk
             sp = wk.lock();
             // sh will be destroyed after this point, but sp is still alive
         }
         // sp still keeps the data alive.
         // At this point we could even call lock() again 
         // to retrieve another shared_ptr to the same data from wk
         assert(*sp == 42);
         assert(!wk.expired());
         // resetting sp will delete the data,
         // as it is currently the last shared_ptr with ownership
         sp.reset();
         // attempting to lock wk now will return an empty shared_ptr,
         // as the data has already been deleted
         sp = wk.lock();
         assert(!sp);
         assert(wk.expired());
     }
}