• stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结


    stl中auto_ptr,unique_ptr,shared_ptr,weak_ptr四种智能指针使用总结

    1. auto_ptr
    auto_ptr主要是用来解决资源自动释放的问题,比如如下代码:
    void Function()
    {
    Obj*p = new Obj(20);
    ...
    if (error occor)
    throw ... 或者 retrun;
    delete p;
    }
    在函数遇到错误之后,一般会抛异常,或者返回,但是这时很可能遗漏之前申请的资源,及时是很有经验的程序员也有可能出现这种错误,
    而使用auto_ptr会在自己的析够函数中进行资源释放。也就是所说的RAII
    使用auto_ptr代码如下
    void Function()
    {
    auto_ptr<Obj> ptr( new Obj(20) );
    ...
    if (error occur)
    throw exception...
    }
    这样无论函数是否发生异常,在何处返回,资源都会自动释放。
    需要提一下的是这是一个被c++11标准废弃的一个智能指针,为什么会被废弃,先看一下下面的代码:
    auto_ptr<Obj> ptr1( new Obj() );
    ptr1->FuncA();
    auto_ptr<Obj> ptr2 = ptr1;
    ptr2->FuncA();
    ptr1->FuncA();  // 这句话会异常
    为什么在把ptr1复制给ptr2之后ptr1再使用就异常了呢?
    这也正是他被抛弃的主要原因。
    因为auto_ptr复制构造函数中把真是引用的内存指针进行的转移,也就是从ptr1转移给了ptr2,此时,ptr2引用了Obj内存地址,而ptr1引用的内存地址为空,、
    此时再使用ptr1就异常了。


    2. unique_ptr
    unique_ptr可以看成是auto_ptr的替代品。因为他对对象的所有权比较专一,所以才叫unique 大笑
    i) 无法进行复制构造和赋值操作
    auto_ptr与unique_ptr的对比:
    auto_ptr<Obj> ap(new Obj() );
    auto_ptr<Obj> one (ap) ; // ok
    auto_ptr<Obj> two = one; //ok


    unique_ptr<Obj> ap(new Obj() );
    unique_ptr<Obj> one (ap) ; // 会出错
    unique_ptr<Obj> two = one; //会出错
    也就是说unique_ptr对对象的引用比较专一,不允许随随便便的进行转移
    ii)  可以进行移动构造和移动赋值操作
    unique_ptr<Obj> GetObj()
    {
    unique<Obj> ptr( new Obj() );
    return ptr;
    }
    unique<Obj> ptr = GetObj();
    上面的代码可以顺利执行!
    什么是移动构造和移动赋值呢? 这得益于C++11的标准规范。这个知识点可以参考参考网址中的第三个。
    那么如果万一我就是需要把一个unique_ptr智能指针赋值给另外一个怎么办呢?
    可以使用移动函数!如下:
    unique<Obj> ptr1( new Obj() );
    unique<Obj> ptr2( std::move(ptr1) );
    这个效果和auto_ptr直接赋值是一样的,就是ptr1不再拥有Obj对象了,所以ptr1不能再用来操作内存中的Obj对象,因为这个是手动操作的,
    所以程序员自己也会更加小心。


    3. shared_ptr
    auto_ptr和unique_ptr都只能一个智能指针引用对象,而shared_ptr则是可以多个智能指针同时拥有一个对象。
    shared_ptr实现方式就是使用引用计数。这一技术在COM中是用来管理COM对象生命周期的一个方式。
    引用计数的原理是,多个智能指针同时引用一个对象,每当引用一次,引用计数加一,每当智能指针销毁了,引用计数就减一,
    当引用计数减少到0的时候就释放引用的对象。这种引用计数的增减发生在智能指针的构造函数,复制构造函数,赋值操作符,析构函数中。
    这种方式使得多个智能指针同时对所引用的对象有拥有权,同时在引用计数减到0之后也会自动释放内存,也实现了auto_ptr和unique_ptr的资源释放的功能。


    void Function()
     {
    shared_ptr<Obj> ptr1( new Obj() ); // 引用计数为1
    {
    shared_ptr<Obj> ptr2( ptr1 ); // 引用计数为2
    {
    shared_ptr<Obj> ptr3 = ptr2; // 引用计数为3
    int e = 0
    }
    //引用计数为2
    }
    //引用计数为1
    }
    //函数返回之后引用计数为0,new 出来的Obj内存已经释放了


    由于shared_ptr支持复制构造,所以他可以作为标准库容器中的元素
    vector<shared_ptr<Obj>> vect;
    for (int i = 0; i < 10; ++i)
    {
    vect.push_back( shared_ptr<Obj>( new Obj() ) );
    }
    vector<shared_ptr<Obj>> vect2 = vect;
    这些操作是auto_ptr和unique_ptr不能实现的。
    注意,智能指针默认使用delete来释放资源,如果资源是FILE*怎么办?释放的时候就需要用fclose了。
    如何实现呢? 
    shared_ptr构造函数可以传递一个删除器。
    FILE* pStm = fopen(...);
    shared_ptr<FILE> fileRes(pStm, &fclose);
    或者使用一个仿函数
    class FileCloser { 
         public: 


            void operator()(FILE* file) { 


             std::cout << "The FileCloser has been called with a FILE*, " 


               "which will now be closed. "; 


             if (file!=0)  


               fclose(file); 


           } 


         }; 
    shared_ptr<FILE> fileRes(pStm, FileCloser);
    关于这点看一参考这篇文章: http://www.cnblogs.com/learn-my-life/p/3817151.html




    4. weak_ptr
    shared_ptr是一种强引用的关系,智能指针直接引用对象。那么这个会代码一个隐含的问题,就是循环引用,从而造成内存泄漏,即便是java语言有自己的
    垃圾回收器,对这种内存泄漏也没有办法,所以循环引用对java程序员来说也是一个很值得注意的问题。首先来看一个循环引用的例子。


    class Parent
    {
    public:
        shared_ptr<Child> child;
    };




    class Child
    {
    public:
        shared_ptr<Parent> parent;
    };


    void Function()
    {
    shared_ptr<Parent> pA(new Parent);
    shared_ptr<Child> pB(new Child);
    pA->child = pB;
    pB->parent = pA;
    }
    现在来分析一下Function函数的执行过程:
    1. 第一条语句使得pA引用了Parent一个指针,Parent引用计数为1
    2. 第二条语句使得pB引用了Child一个指针,Child引用计数为1
    3. 第三条语句,调用了shared_ptr<Child>类的赋值操作符,使得Child引用计数变为2
    4. 第四条语句,调用了shared_ptr<Parent>类的赋值操作符,使得Parent引用计数变为2
    5. 函数返回之前调用了shared_ptr<Parent>和shared_ptr<Child>类的析够函数,使得Child引用计数变为1,Parent引用计数变为1
    看!函数执行完之后new出来的Parent和Child并没有释放,所以出现了内存泄漏。
    出现泄漏的原因就是pA和pB相互引用了,导致两者所引用对象的引用计数不能减少到0,造成泄漏。
    如果把第三条语句或者第四条语句任意删除一个,就不会有泄漏了。
    这就是强引用所带来的问题。
    weak_ptr从字面意思上可以看出是一个弱指针,不是说明这个指针的能力比较弱,而是说他对他所引用的对象的所有权比较弱,
    说得更直接一点儿就是他并不拥有所引用对象的所有权,而且他还不能直接使用他所引用的对象。


    在stl中,weak_ptr是和shared_ptr配合使用的,在实现shared_ptr的时候也就考虑了weak_ptr的因素。
    weak_ptr是shared_ptr的观察者,它不会干扰shared_ptr所共享对象的所有权,
    当一个weak_ptr所观察的shared_ptr要释放它的资源时,它会把相关的weak_ptr的指针设置为空,防止weak_ptr持有悬空的指针。
    注意:weak_ptr并不拥有资源的所有权,所以不能直接使用资源。
    可以从一个weak_ptr构造一个shared_ptr以取得共享资源的所有权。
    void Function()
    {
     shared_ptr<int> sp( new Obj() );
     assert(sp.use_count() == 1);
     weak_ptr<int> wp(sp); //从shared_ptr创建weak_ptr
     assert(wp.use_count() == 1);
     if (!wp.expired())//判断weak_ptr观察的对象是否失效
     {
      shared_ptr<int> sp2 = wp.lock();//获得一个shared_ptr
      *sp2 = 100;
      assert(wp.use_count() == 2);
     }
     assert(wp.use_count() == 1);
     return 0;
    }
    weak_ptr并没有重载-> 和 * 操作符,所以我们不能通过他来直接使用资源,我们可以通过lock来获得一个shared_ptr对象
    来对资源进行使用,如果引用的资源已经释放,lock()函数将返回一个存储空指针的shared_ptr。 expired函数用来判断资源是否失效。
    使用weak_ptr并不会增加资源的引用计数。所以对资源的引用是弱引用,利用这个特性可以解决前面所说的循环依赖问题。


    class Parent
    {
    public:
        weak_ptr<Child> child;
    };




    class Child
    {
    public:
        weak_ptr<Parent> parent;
    };


    void Function()
    {
    shared_ptr<Parent> pA(new Parent);
    shared_ptr<Child> pB(new Child);
    pA->child = pB;
    pB->parent = pA;
    }


    这个时候第三和第四条语句的执行并没有增加引用计数,从而在函数执行完成只有能自动释放内存。
    从上面的分析可以看出,weak_ptr是一种辅助shared_ptr的一种智能指针,一般不单独使用,而是结合
    shared_ptr一起使用。


    总结:
    1. 尽量使用unique_ptr而不要使用auto_ptr
    2. 一般来说shared_ptr能够满足我们大部分的需求
    3. weak_ptr可以避免递归的依赖关系


    参考网址:
    1. http://www.jellythink.com/archives/684
    2. http://www.jellythink.com/archives/673
    3. http://www.cnblogs.com/TianFang/archive/2013/01/26/2878356.html (c++右值引用相关知识点)
    4. http://www.cnblogs.com/learn-my-life/p/3817151.html
    5. http://blog.csdn.net/hp_truth/article/details/40511617
    6. http://www.cppblog.com/deane/archive/2010/02/25/108428.html(c++临时对象相关知识点)
    7. http://blog.csdn.net/mmzsyx/article/details/8090849
    8. http://www.cnblogs.com/learn-my-life/p/3817279.html

  • 相关阅读:
    nodejs 单元测试框架api文档
    Pytest官方教程-22-API参考-Hooks
    【pytest】Hook 方法之 pytest_addoption :注册命令行参数
    k8s架构
    k8s 组件介绍-API Server API Server简介
    SpringCloud 应用在 Kubernetes 上的最佳实践 — 线上发布(可灰度)
    Elasticsearch 常见的 8 种错误及最佳实践
    Istio路由配置
    监控系统选型,这篇不可不读!
    logstash实现日志文件同步到elasticsearch深入详解
  • 原文地址:https://www.cnblogs.com/lsgxeva/p/7668868.html
Copyright © 2020-2023  润新知