码迷,mamicode.com
首页 > 其他好文 > 详细

四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr

时间:2017-07-22 16:50:12      阅读:713      评论:0      收藏:0      [点我收藏+]

标签:log   释放   ...   观察   ==   let   循环引用   share   assert   

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

(1)auto_ptr

主要用于解决资源自动释放的问题。防止用户忘记delete掉new申请的内存空间。使用auto_ptr会在离开变量的作用域之后直接调用析构函数进行资源释放。

void Function()

{

    auto_ptr<Obj> ptr(new Obj(20));

    ...

    if (error occur)

         throw exception...

}

但是,这是一种被c++11标准废弃的一个智能指针,原因是一旦将autoptr对象赋值给另一个对象,原来的智能指针所指的将是空的地址。再使用该指针就是错误的了。

 

(2)unique_ptr

unique_ptr指针可以看成auto_ptr的替代品。因为他对对象的引用是唯一的,不能随便进行转移。这种专一是通过无法进行unique_ptr对象的赋值和拷贝构造来实现的。

另外,unique_ptr可以进行移动构造和移动赋值操作。

如果一定要用unique_ptr实现auto_ptr的功能,也就是一定要把一个unique_ptr赋值给另一个,那就一定要通过移动函数来实现。由于是手动操作,因此需要程序员格外注意。

unique<Obj> ptr1(new Obj());

unique<Obj> ptr2(std::move(ptr1));

 

(3)shared_ptr

auto_ptr和unique_ptr都只能一个智能指针引用对象,而shared_ptr则是可以多个智能指针同时拥有一个对象。

shared_ptr实现方式就是使用引用计数。引用计数的原理是,多个智能指针同时引用一个对象,每当引用一次,引用计数加一,每当智能指针销毁了,引用计数就减一,当引用计数减少到0的时候就释放引用的对象。这种引用计数的增减发生在智能指针的构造函数,复制构造函数,赋值操作符,析构函数中。

这种方式使得多个智能指针同时对所引用的对象有拥有权,同时在引用计数减到0之后也会自动释放内存,也实现了auto_ptr和unique_ptr的资源释放的功能。

由于shared_ptr支持复制构造,所以它可以作为标准库容器中的元素。这是auto_ptr和unique_ptr所不能实现的。

 

(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); //Parent的引用计数为1

    shared_ptr<Child*> pB(new Child); //Child引用计数为1

    pA->child = pB; //使用了赋值函数,因此Child引用计数增加1,变为2

    pB->parent = pA; //使用了赋值函数,因此Parent引用计数增加1,变为2

// pB先出作用域,pB的引用计数减少为1,不为0,因此堆上的pb所指空间没有被释放;同理pA

}

所以在使用基于引用计数的智能指针时,要特别小心循环引用带来的内存泄漏,循环引用不只是两方的情况,只要引用链成环都会出现问题。当然循环引用本身就说明设计上可能存在一些问题。

这就是使用强引用所带来的问题。使用weak_ptr解决

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可以避免递归的依赖关系

四种智能指针:auto_ptr,unique_ptr,shared_ptr,weak_ptr

标签:log   释放   ...   观察   ==   let   循环引用   share   assert   

原文地址:http://www.cnblogs.com/sjqiu/p/7221537.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!