博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

C++ shared_ptr 循环引用泄露问题

Posted on 2020-10-20 18:05  凤凰火舞  阅读(133)  评论(0编辑  收藏  举报

用一段代码来展示shared_ptr可能存在的内存泄露问题:

  1 /// 思路来源: 极客时间 https://time.geekbang.org/column/article/239580
  2 
  3 #include <iostream>
  4 
  5 using namespace std;
  6 
  7 class Node final
  8 {
  9 public:
 10     using this_type = Node;
 11     using shared_type = shared_ptr<this_type>;
 12 public:
 13     shared_type next; // 使用智能指针指向下一个节点
 14     string name;
 15 public:
 16     Node(string _name)
 17     {
 18         name = _name;
 19         cout << "Node constructor, name=" << name.c_str() << endl;
 20     }
 21 
 22     ~Node()
 23     {
 24         cout << "Node destructor, name=" << name.c_str() << endl;
 25     }
 26 };
 27 
 28 void LeakTest()
 29 {
 30     // 创建内存块 M_N1
 31     auto n1 = make_shared<Node>("n1");
 32     // 创建内存块 M_N2
 33     auto n2 = make_shared<Node>("n2");
 34     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 35 
 36     n1->next = n2;
 37     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 38 
 39     n2->next = n1;
 40     // M_N1.ref==2, M_N2.ref==2;
 41     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 42     // 1. n1先销毁, 则M_N1的内存引用计数变为1, 还被n2->next引用
 43     // 2. n2销毁, 则M_N2的内存引用计数变为1, 还被n1->next引用
 44     // 3. 产生内存泄露
 45     
 46     /// 最终输出
 47     /// Node constructor, name=n1
 48     /// Node constructor, name = n2
 49     /// n1.ref = 1, n2.ref = 1
 50     /// n1.ref = 1, n2.ref = 2
 51     /// n1.ref = 2, n2.ref = 2
 52 }
 53 
 54 void NormalTestN1()
 55 {
 56     // 创建内存块 M_N1
 57     auto n1 = make_shared<Node>("n1");
 58     // 创建内存块 M_N2
 59     auto n2 = make_shared<Node>("n2");
 60     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 61 
 62     n1->next = n2;
 63     // M_N1.ref==1, M_N2.ref==2;
 64     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 65     // 1. n1先销毁, 则M_N1的内存引用计数变为0, 内存块M_N1需要销毁, 则此时会走到了n1的析构方法, n1析构时n1->next也被销毁, 此时n1->next(即内存块M_N2)引用计数变为1
 66     // 2. n2销毁, 则M_N2的内存引用计数变为0, 内存块M_N2需要销毁
 67     // 3. 无内存泄露
 68 
 69     /// 最终输出
 70     /// Node constructor, name=n1
 71     /// Node constructor, name = n2
 72     /// n1.ref = 1, n2.ref = 1
 73     /// n1.ref = 1, n2.ref = 2
 74     /// Node destructor, name = n1
 75     /// Node destructor, name = n2
 76 }
 77 
 78 void NormalTestN2()
 79 {
 80     // 创建内存块 M_N1
 81     auto n1 = make_shared<Node>("n1");
 82     // 创建内存块 M_N2
 83     auto n2 = make_shared<Node>("n2");
 84     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 85 
 86     n2->next = n1;
 87     // M_N1.ref==1, M_N2.ref==2;
 88     cout << "n1.ref=" << n1.use_count() << ", n2.ref=" << n2.use_count() << endl;
 89     // 1. n1先销毁, 则M_N1的内存引用计数变为1
 90     // 2. n2销毁, 则M_N2的内存引用计数变为0, 内存块M_N2需要销毁, 则此时会走到了n2的析构方法, n2析构时n2->next也被销毁, 此时n2->next(即内存块M_N1)引用计数变为0
 91     // 3. 此时M_N1被销毁, 走到M_N1的析构函数
 92     // 4. 无内存泄露
 93 
 94     /// 最终输出
 95     /// Node constructor, name=n1
 96     /// Node constructor, name = n2
 97     /// n1.ref = 1, n2.ref = 1
 98     /// n1.ref = 2, n2.ref = 1
 99     /// Node destructor, name = n2
100     /// Node destructor, name = n1
101 }
102 
103 int main()
104 {
105     cout << "\n==========LeakTest Begin==========" << endl;
106     LeakTest();
107     cout << "==========LeakTest End==========" << endl;
108 
109     cout << "\n==========NormalTestN1 Begin==========" << endl;
110     NormalTestN1();
111     cout << "==========NormalTestN1 End==========" << endl;
112 
113     cout << "\n==========NormalTestN2 Begin==========" << endl;
114     NormalTestN2();
115     cout << "==========NormalTestN2 End==========" << endl;
116 
117     return 0;
118 }

解决方案:使用weak_ptr