• shared_ptr 的使用及注意事项


    1. 声明
    #include <boost/shared_ptr.hpp>

    class UsersBitmap {

    ...

    }

    typedef boost::shared_ptr<UsersBitmap> UsersBitmapPtr;

    2. 使用

    UsersBitmapPtr login_users_;
    UsersBitmapPtr temp_login_users(new UsersBitmap());    //指向对象

    login_users_.reset(new UsersBitmap());     //指针指向新的地方

    login_users_.reset();  //指针置空

    ///////////////////////////////////////////////////////////////////////////

    ////////////////////////////////////

    虽然boost.shared_ptr是个非常好的东西,使用它可以使得c++程序不需要考虑内存释放的问题,但是还是有很多必须注意的地方。下面罗列了一些本人在实际工作中经常碰到的使用shared_ptr出问题的几种情况。 

    1. shared_ptr多次引用同一数据,如下:
    {
    int* pInt = new int[100];
    boost::shared_ptr<int> sp1(pInt);
    // 一些其它代码之后…
    boost::shared_ptr<int> sp2(pInt);
    }

    这种情况在实际中是很容易发生的,结果也是非常致命的,它会导致两次释放同一块内存,而破坏堆。 

    2.使用shared_ptr包装this指针带来的问题,如下:

    class tester 
    {
    public:
      tester()
      ~tester()
      {
        std::cout << "析构函数被调用! "; 
      }
    public:
      boost::shared_ptr<tester> sget()
      {
        return boost::shared_ptr<tester>(this);
      }
    };

    int main()
    {
      tester t;
      boost::shared_ptr<tester> sp =  t.sget(); // …
      return 0;
    }

    也将导致两次释放t对象破坏堆栈,一次是出栈时析构,一次就是shared_ptr析构。若有这种需要,可以使用下面代码。

    class tester : public boost::enable_shared_from_this<tester>
    {
    public:
      tester()
      ~tester()
      {
      std::cout << "析构函数被调用! "; 
      }
    public:
      boost::shared_ptr<tester> sget()
      {
      return shared_from_this();
      }
    };

    int main()
    {
      boost::shared_ptr<tester> sp(new tester);
      // 正确使用sp 指针。
      sp->sget();
      return 0;
    }

    3. shared_ptr循环引用导致内存泄露,代码如下:

    class parent;
    class child; 

    typedef boost::shared_ptr<parent> parent_ptr;
    typedef boost::shared_ptr<child> child_ptr; 

    class parent
    {
    public:
           ~parent() { 
                  std::cout <<"父类析构函数被调用. "; 
           }
    public:
           child_ptr children;
    };

    class child
    {
    public:
           ~child() { 
                  std::cout <<"子类析构函数被调用. "; 
           }
    public:
           parent_ptr parent;
    };

    int main()
    {
      parent_ptr father(new parent());
      child_ptr son(new child);
      // 父子互相引用。
      father->children = son;
      son->parent = father;
      return 0;
    }

    如上代码,将在程序退出前,father的引用计数为2,son的计数也为2,退出时,shared_ptr所作操作就是简单的将计数减1,如果为0则释放,显然,这个情况下,引用计数不为0,于是造成father和son所指向的内存得不到释放,导致内存泄露。 

    4. 在多线程程序中使用shared_ptr应注意的问题。代码如下:

    class tester 
    {
    public:
      tester() {}
      ~tester() {}
      // 更多的函数定义…
    };

    void fun(boost::shared_ptr<tester> sp)
    {
      // !!!在这大量使用sp指针.
      boost::shared_ptr<tester> tmp = sp;
    }

    int main()
    {
      boost::shared_ptr<tester> sp1(new tester);
      // 开启两个线程,并将智能指针传入使用。
      boost::thread t1(boost::bind(&fun, sp1));
      boost::thread t2(boost::bind(&fun, sp1));
      t1.join();
      t2.join();
      return 0;
    }

    这个代码带来的问题很显然,由于多线程同是访问智能指针,并将其赋值到其它同类智能指针时,很可能发生两个线程同时在操作引用计数(但并不一定绝对发生),而导致计数失败或无效等情况,从而导致程序崩溃,如若不知根源,就无法查找这个bug,那就只能向上帝祈祷程序能正常运行。

    可能一般情况下并不会写出上面这样的代码,但是下面这种代码与上面的代码同样,如下:

    class tester 
    {
    public:
      tester() {}
      ~tester() {}
    public:
      boost::shared_ptr<int> m_spData; // 可能其它类型。
    };

    tester gObject;

    void fun(void)
    {
      // !!!在这大量使用sp指针.
      boost::shared_ptr<int> tmp = gObject.m_spData;
    }

    int main()
    {
      // 多线程。
      boost::thread t1(&fun);
      boost::thread t2(&fun);
      t1.join();
      t2.join();
      return 0;
    }

    情况是一样的。要解决这类问题的办法也很简单,使用boost.weak_ptr就可以很方便解决这个问题。第一种情况修改代码如下:

    class tester 
    {
    public:
      tester() {}
      ~tester() {}
      // 更多的函数定义…
    };

    void fun(boost::weak_ptr<tester> wp)
    {
      boost::shared_ptr<tester> sp = wp.lock;
      if (sp)
      {
        // 在这里可以安全的使用sp指针.
      }
      else
      {
        std::cout << “指针已被释放!” << std::endl;
      }


    int main()
    {
      boost::shared_ptr<tester> sp1(new tester);
      boost.weak_ptr<tester> wp(sp1);
      // 开启两个线程,并将智能指针传入使用。
      boost::thread t1(boost::bind(&fun, wp));
      boost::thread t2(boost::bind(&fun, wp));
      t1.join();
      t2.join();
      return 0;
    }

    boost.weak_ptr指针功能一点都不weak,weak_ptr是一种可构造、可赋值以不增加引用计数来管理shared_ptr的指针,它可以方便的转回到shared_ptr指针,使用weak_ptr.lock函数就可以得到一个shared_ptr的指针,如果该指针已经被其它地方释放,它则返回一个空的shared_ptr,也可以使用weak_ptr.expired()来判断一个指针是否被释放。

    boost.weak_ptr不仅可以解决多线程访问带来的安全问题,而且还可以解决上面第三个问题循环引用。Children类代码修改如下,即可打破循环引用:

    class child
    {
    public:
      ~child() { 
       std::cout <<"子类析构函数被调用. "; 
      }
    public:
      boost::weak_ptr<parent> parent;
    };

    因为boost::weak_ptr不增加引用计数,所以可以在退出函数域时,正确的析构。

  • 相关阅读:
    Python爬虫教程-06-爬虫实现百度翻译(requests)
    Python爬虫教程-04-response简介
    Python爬虫教程-05-python爬虫实现百度翻译
    LeetCode——Balanced Binary Tree
    LeetCode——Min Stack
    LeetCode——Count and Say
    LeetCode——Invert Binary Tree
    LeetCode——Contains Duplicate II
    设计模式——桥接模式
    设计模式——责任链模式
  • 原文地址:https://www.cnblogs.com/xumaojun/p/8521617.html
Copyright © 2020-2023  润新知