zoukankan      html  css  js  c++  java
  • 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 << "
    ==========LeakTest Begin==========" << endl;
    106     LeakTest();
    107     cout << "==========LeakTest End==========" << endl;
    108 
    109     cout << "
    ==========NormalTestN1 Begin==========" << endl;
    110     NormalTestN1();
    111     cout << "==========NormalTestN1 End==========" << endl;
    112 
    113     cout << "
    ==========NormalTestN2 Begin==========" << endl;
    114     NormalTestN2();
    115     cout << "==========NormalTestN2 End==========" << endl;
    116 
    117     return 0;
    118 }

    解决方案:使用weak_ptr

  • 相关阅读:
    WPF常用TriggerAction用法 (一)
    一个WPF只能输入数字的行为。
    自定义panel实现,并实现item更改和移除动画。
    MVVM模式下弹出窗体
    ZAM 3D 制作简单的3D字幕 流程(二)
    ZAM 3D 制作简单的3D字幕 流程(一)
    ZAM 3D 制作3D动画字幕 用于Xaml导出
    Metro Win8风格的按钮(Filp翻转)
    WPF自动隐藏的消息框(鼠标放上去将一直显示,移开动画继续),提供normal和error两种边框。
    可分组的选择框控件(MVVM下)(Toggle样式 仿造单选框RadioButton,复选框CheckBox功能)
  • 原文地址:https://www.cnblogs.com/qinruijie/p/13848012.html
Copyright © 2011-2022 走看看