用一段代码来展示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