C++学习笔记 22

来源:互联网 发布:数据可视化分析 编辑:程序博客网 时间:2024/06/05 19:06

auto_ptr

在C++中, auto_ptr是一个类,它用来实现对动态分配对象的自动释放。
构造函数与析构函数:auto_ptr在构造时获取对某个对象的所有权(ownership), 在析构时释放该对象。我们可以这样使用auto_ptr来提高代码安全性,类似下面的代码。

int*p=new int(0);auto_ptr<int> ap(p);

从此我们不必关心应该何时释放p,也不用担心发生异常会有内存泄漏。这里我们有几点要注意:
1) 因为auto_ptr析构的时候肯定会删除他所拥有的那个对象,所以我们就要注意了,一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象。像这样:

int*p=new int(0);auto_ptr<int>ap1(p);auto_ptr<int>ap2(p);

因为ap1与ap2都认为指针p是归它管的,在析构时都试图删除p, 两次删除同一个对象的行为在C++标准中是未定义的。所以我们必须防止这样使用auto_ptr。

2) 考虑下面这种用法:

int*pa=new int[10];auto_ptr<int>ap(pa);

因为auto_ptr的析构函数中删除指针用的是delete,而不是delete [],所以我们不应该用auto_ptr来管理一个数组指针。

3) 构造函数的explicit关键词有效阻止从一个”裸”指针隐式转换成auto_ptr类型。

4) 因为C++保证删除一个空指针是安全的, 所以我们没有必要把析构函数写成:

~auto_ptr()throw(){    if(ap)delete ap;}

5) 拷贝构造与赋值
与引用计数型智能指针不同的,auto_ptr要求其对”裸”指针的完全占有性。也就是说一个”裸”指针不能同时被两个以上的auto_ptr所拥有。那么,在拷贝构造或赋值操作时,我们必须作特殊的处理来保证这个特性。auto_ptr的做法是”所有权转移”,即拷贝或赋值的源对象将失去对”裸”指针的所有权,所以,与一般拷贝构造函数,赋值函数不同, auto_ptr的拷贝构造函数,赋值函数的参数为引用而不是常引用(const reference)。当然,一个auto_ptr也不能同时拥有两个以上的”裸”指针,所以,拷贝或赋值的目标对象将先释放其原来所拥有的对象。
这里的注意点是:
1.因为一个auto_ptr被拷贝或被赋值后, 其已经失去对原对象的所有权,这个时候,对这个auto_ptr的提领(dereference)操作是不安全的。如下:

int*p=new int(0);auto_ptr<int>ap1(p);auto_ptr<int>ap2=ap1;cout<<*ap1;//错误,此时ap1只剩一个null指针在手了

这种情况较为隐蔽的情形出现在将auto_ptr作为函数参数按值传递,因为在函数调用过程中在函数的作用域中会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参auto_ptr就失去了其对原对象的所有权,而该对象会在函数退出时被局部auto_ptr删除。如下:

void f(auto_ptr<int>ap){    cout<<*ap;}auto_ptr<int>ap1(new int(0));f(ap1);cout<<*ap1;//错误,经过f(ap1)函数调用,ap1已经不再拥有任何对象了。

因为这种情况太隐蔽,太容易出错了,所以auto_ptr作为函数参数按值传递是一定要避免的。或许大家会想到用auto_ptr的指针或引用作为函数参数或许可以,但是仔细想想,我们并不知道在函数中对传入的auto_ptr做了什么, 如果当中某些操作使其失去了对对象的所有权, 那么这还是可能会导致致命的执行期错误。
也许,用const reference的形式来传递auto_ptr会是一个不错的选择。

建议参考auto_ptr提供的源码,总的来说这个智能指针不太好用,更好用的是share_ptr。

share_ptr

出现原因:*auto_ptr由于它的破坏性复制语义*,无法满足标准容器对元素的要求,因而不能放在标准容器中;如果我们希望当容器析构时能自动把它容纳的指针元素所指的对象删除时,通常采用一些间接的方式来实现,显得比较繁琐。boost库中提供了一种新型的智能指针shared_ptr,它解决了在多个指针间共享对象所有权的问题,同时也满足容器对元素的要求,因而可以安全地放入容器中。

shared_ptr最初实现于Boost库中,后来被C++标准委员会收录于TR1技术报告中,成为C++11标准的一部分。

shared_ptr是一种智能指针(smart pointer),作用有如同指针,但会记录有多少个shared_ptrs共同指向一个对象。这便是所谓的引用计数(reference counting)。一旦最后一个这样的指针被销毁,也就是一旦某个对象的引用计数变为0,这个对象会被自动删除。这在非环形数据结构中防止资源泄露很有帮助。

参考shared_ptr 写的很详细。

总的来说,auto_ptr和shared_ptr的差别就在于复制性语义,后者有一个计数指针,更加方便的使用。

shared_ptr的线程安全性

shared_ptr 的线程安全级别和内建类型、标准库容器、std::string 一样:
1、一个 shared_ptr 对象实体可被多个线程同时读取;

2、两个 shared_ptr 对象实体可以被两个线程同时写入,“析构”算写操作;

3、如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

因为 shared_ptr 有两个数据成员,读写操作不能原子化,使得多线程读写同一个 shared_ptr 对象需要加锁。

循环引用的后果及如何避免、weak_ptr的作用?

循环引用是指进行了自引用,引用计数增加所致(类似死锁),计数器不会降低为0,也不会调用析构函数,会造成内存泄漏。解决办法:引入weak_ptr弱引用指针即可解决循环引用问题。weak_ptr不会修改引用计数。

weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。

参考这个链接C++11智能指针之weak_ptr。

原创粉丝点击