CppCoreGuidelines R.24 用 std::weak_ptr 打破 shared_ptr 循环
24 February 2023
“Use std::weak_ptr
to break cycles of shared_ptr
’s”
理由
shared_ptr
依赖于使用计数,如果我们的数据结构是一种循环结构,你依赖我,我依赖你,那么使用计数就永远不会减到 0。所以我们需要一种机制去破坏这种循环结构。
std::weak_ptr
可以临时指向一个 std::shared_ptr
,作为一个临时所有者,不增加其使用计数。这样,就不会阻止别的代码删除这个这个对象。一旦这个对象的使用计数清零, weak_ptr
指向的对象就没了。这就要求我们在使用这个指针的时候,先判断对象是否还存在,可以通过 lock()
函数锁住对象,然后访问使用。
比如
#include <memory> class bar; class foo { public: explicit foo(const std::shared_ptr<bar>& forward_reference) : forward_reference_(forward_reference) { } private: std::shared_ptr<bar> forward_reference_; }; class bar { public: explicit bar(const std::weak_ptr<foo>& back_reference) : back_reference_(back_reference) { } void do_something() { if (auto shared_back_reference = back_reference_.lock()) { // Use *shared_back_reference } } private: std::weak_ptr<foo> back_reference_; };
再比如以下代码,树的结构就是一种循环结构。当我们把树的根节点设置成
weak_ptr
的时候,就可以通过 reset 根节点来销毁这棵树。
#include <memory> #include <vector> struct TreeNode { std::weak_ptr<TreeNode> parent; std::vector< std::shared_ptr<TreeNode> > children; ~TreeNode() { cout << "destroying node\n"; } }; int main() { // Create a TreeNode as the root auto root = make_shared<TreeNode>(); // Give the parent 10 child nodes. for (size_t i = 0; i < 10; ++i) { auto child = make_shared<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(); }
destroying node destroying node destroying node destroying node destroying node destroying node destroying node destroying node destroying node destroying node destroying node
强化如果可以静态检测到这种循环,就不需要 weak_ptr
了。