zoukankan      html  css  js  c++  java
  • c++ shared_ptr的使用

    shared_ptr.是c++为了提高指针安全性而添加的智能指针,方便了内存管理。功能非常强大,非常强大,非常强大(不单单是shared_ptr,配合week_ptr以及enable_share_from_this()以及share_from_this())!!!对于支持智能指针的c++版本编程,能用智能指针就用智能指针!

    shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。使得指针可以共享对象,并且不用考虑内存泄漏问题
    shared_ptr 可以支持普通指针的所有操作,完全可以像操作普通指针一样操作智能指针。
    shared_ptr 可以通过三种方式得到(拷贝初始化,定义delete操作的方式不在罗列,只讨论初始化指针所指对象来源):
    1.通过一个指向堆上申请的空间的指针初始化(切记不要用栈上的指针,否则,当智能指针全部释放控制权(栈中的对象离开作用域本身就会析构一次),将会析构对象,导致出错)
    2.通过make_shared函数得到
    3.通过另外一个智能指针初始化
    #include <memory>  
    #include <iostream>  
    int main()  
    {  
        int *p = new int(30);  
        std::shared_ptr<int> bptr(p);//方式1  
        std::shared_ptr<int> aptr = std::make_shared<int>(20);//方式  
        std::shared_ptr<int> cptr(aptr);  
        std::cout << "aptr.use_count() = " << aptr.use_count() <<"  value = "<<*aptr<<std::endl;//use_count 是引用计数器  
        std::cout << "bptr.use_count() = " << bptr.use_count() <<"  value = "<<*bptr<<std::endl;  
        std::cout << "cptr.use_count() = " << cptr.use_count() <<"  value = "<<*cptr<<std::endl;  
        //输出是:2,20  
        //       1,30  
        //       2,20  
    }   

    下面是使用shared_ptr 的一些注意事项:

    0. 禁止纯指针给智能指针赋值或者拷贝构造。

    int* a=new int(2);    
    shared_ptr<int>sp=a;//  error    
    sp=a;//    error   
    

      

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

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

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

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

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

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

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

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

    class parent;
    class child; 

    typedef shared_ptr<parent> parent_ptr;
    typedef 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(shared_ptr<tester> sp)
    {
      // !!!在这大量使用sp指针.
      shared_ptr<tester> tmp = sp;
    }

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

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

    引入weak_ptr可以解决这个问题,将fun函数修改如下:


    void fun(weak_ptr<tester> wp)
    {

    //这个方案只解决了多线程对引用计数同时访问的读写问题,并没有解决对share_ptr指向的数据的多线程安全问题,因此weak_ptr只是安全的获得share_ptr的一种方式,因为可以确保在获得share_ptr的时候的多线程安全
      shared_ptr<tester> sp = wp.lock;
      if (sp)
      {
        // 在这里可以安全的使用sp指针.
      }
      else
      {
        std::cout << “指针已被释放!” << std::endl;
      }


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

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

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

    weak_ptr 介绍:

    weak_ptr是为了配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况.
    用法:
     weak_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。
     使用weak_ptr的成员函数use_count()可以观测资源的引用计数,另一个成员函数expired()的功能等价于use_count()==0,但更快,表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。
     weak_ptr可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true的时候,lock()函数将返回一个存储空指针的shared_ptr.

    share_ptr 的 aliasing constructor:

    可以使得share_ptr 拥有某个对象控制权的时候,存储指针(storage pointer,shared_ptr 还有一个概念叫owned ptr 指向存储当前控制对象相关信息)指向另外一个对象,模板如下:

    template <class U> shared_ptr (const shared_ptr<U>& x, element_type* p) noexcept;

      该构造函数是的shared_ptr 管理 x 指针指向对象,会将计数器加一,并且在释放控制权时减一,如有需要还会调用析构函数,但是不同的是它storage ptr 指向的是p,调用 share_ptr.get() 会返回 p ,一般的用途在 p所指对象是x所指对象的成员或者别名(alias)[这段是翻译的,总之,aliasing constructor 还是慎用吧]

     本文转载于:https://blog.csdn.net/man_sion/article/details/77196766

  • 相关阅读:
    Java使用Apache Commons Exec运行本地命令行命令
    Win10安装PostgreSQL9.6
    软件工程学习笔记——软件工具
    软件工程学习笔记——软件过程模型
    软件工程学习笔记——软件过程
    软件工程学习笔记——软件生存周期
    软件工程学习笔记——软件工程基本原理
    MySQL学习笔记——MySQL5.7的启动过程(一)
    Windows下免费的屏幕录制软件——EV录屏——推荐
    SecureCRT 8.1破解方式
  • 原文地址:https://www.cnblogs.com/curo0119/p/8919990.html
Copyright © 2011-2022 走看看