1. 程式人生 > 實用技巧 >C++ shared_ptr 迴圈引用洩露問題

C++ shared_ptr 迴圈引用洩露問題

用一段程式碼來展示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