auto_ptr的设计动机:
对于一般的常规指针,在动态分配内存时需要手动的delete掉所占内存,但是难免有疏忽的时候,那么将会对程序造成致命的危险,所以auto_ptr智能指针便应运而生,他本身是一个对象,因为具有常规指针的特点,所以叫做“智能指针”。他在构造函数中new出来内存,在析构函数中delete掉内存,所以不用我们手动的释放内存,带来了一定的安全性。
auto_ptr按照以下的模式进行:
1:获取一些资源。
2:执行一些动作。
3:释放所获取的资源。
对比常规指针示例:
常规指针:
ClassA *ptr=new ClassA;
```````````````````中间步骤`````````
Delete ptr;
智能指针:
Std::auto_ptr<ClassA> ptr(new ClassA);
上面这两个比较一看好像没什么问题,都做得比较完美,实则不然。使用常规指针时,若在中间步骤时发生异常,那么程序就会终止,再也无法执行到最后一步delete了,为ptr分配的内存再也无法访问咯,这是常规指针很难避免的问题(当然可以在中间步骤加上捕获异常的代码来处理意外情况)。而使用auto_ptr则会在发生异常时析构auto_ptr对象,相关联的auto_ptr对象没了,那该对象也就没有了。
auto_ptr详解:
1:auto_ptr的定义以及初始化。
auto_ptr在定义时的初始化必须使用直接法赋值,而不能使用间接法
std::auto_ptr<ClassA> ptr(new ClassA); 正确
std::auto_ptr<ClassA> ptr=new ClassA; 错误
注:至于为什么不能用间接赋值法,知道auto_ptr源码的就会发现它的赋值使用了关键字explicit,即只能显式的赋值,不能由’=’来隐式的赋值。同时请注意我说的是在定义的时候才不能利用间接赋值。,
在程序中的auto_ptr的赋值也有一定的忌讳:
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2;
ptr2=ptr1; 正确
ptr2=new ClassA; 错误,因为new ClassA是一个常规指针
ptr2=std::auto_ptr<ClassA>(new ClassA); 这样才正确。
也就是只有两个auto_ptr的智能指针才能相互赋值,常规指针不能(一个是对象,一个是指针,很好理解吧)
2:auto_ptr智能指针的拥有权转移(auto_ptr最重要的地方)
auto_ptr是这样一种指针,它是它所指向对象的拥有者,他要求每个对象只能有一个拥有者,严禁一物二主。
仔细分析一下可以发现,一个auto_ptr会删除其指向的对象,所以一个对象绝对不能被两个智能指针拥有,那意味着他可能会被销毁两次,这将导致严重后果。所以设计STL的人员采用了拥有权这一概念。
拥有权指的是一个auto_ptr指针对他所指向的对象全权拥有,当使用赋值或者传参的时候,他将会交出拥有权给其他指针,这点稍后讨论。
std::auto_ptr<ClassA> ptr1(new ClassA);
这条语句执行完后,新建出来的对象被ptr1所拥有。
std::auto_ptr<ClassA> ptr2(ptr1);
在这里ptr1把它对该对象的拥有权交给了ptr2,ptr1不再拥有该对象拥有权
这样的话,该对象只会被delete掉一次——在ptr2销毁的时候。
赋值动作也是如此:
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2;
ptr2=ptr1; 同上,拥有权的转移。;
如果在拥有权转交给ptr2时,ptr1已经拥有了一个对象,那么他会调用delete删除那个对象。
std::auto_ptr<ClassA> ptr1(new ClassA);
std::auto_ptr<ClassA> ptr2(new ClassA);
ptr2=ptr1;
ptr1 ptr2都已经拥有一个对象,在ptr1转交拥有权给ptr2时,ptr2会调用delete销毁原有对象,接受新对象,ptr1丢失拥有权后,就变得两手空空,只剩下一个null指针了,所以要注意别在继续用ptr1.
auto_ptr当做函数参数进行传递时涉及的拥有权转移问题:
大家都知道在函数进行传递时有两种情况,一个是作为参数传递进函数,另一个是作为返回值返回,那么对于auto_ptr而言,这些情况又会出现什么问题勒?接下来我们分析一下。
在前面的讲解中,我们已经知道auto_ptr在赋值时会转移拥有权,那么当然在作为参数传递或者作为返回值时也会发生拥有权的转移。我们先来看一段代码:
std::auto_ptr<ClassA> f()
{
std::auto_ptr<ClassA> ptr(new ClassA);
return ptr;
}
void main()
{
std::auto_ptr<ClassA> p;
for(int i=0;i<10;i++)
p=f();
..............
..........
}
每次调f()函数都会创建一个ClassA对象,并赋给ptr指针,然后ptr连同对象和拥有权都一并交给p智能指针。在这样一个返回值类型为auto_ptr指针的情况下,他会发生拥有权的转移。
下面看另一段代码:
template<typename T>
void display(std::auto_ptr<T> p)
{
if(p.get()==NULL)
exit(EXIT_FAILURE);
else
cout<<*p<<endl;
}
首先说明一点,p.get()函数返回的是与p相关联的对象的地址,这点不同于常规指针,需要用一个成员函数来获取。
有人想利用这个函数作为p的输出函数,却没想到这几乎引发了一场灾难,每次调用那个函数之后,程序中已经把拥有权转移到函数里的p上面了,而该函数执行完后并没有返回p,所以函数结束p被删除,相关联的对象也被销毁。这就引发了严重的问题。
通过上面那两个例子,相信大家已经初步理解在参数和返回值问题上auto_ptr的拥有权转移问题了,在实际程序中,若你真的不想转移他的拥有权,让他一直绑定在最初的指针上,你可以这样做:
Const std::auto_ptr<ClassA> p(new ClassA);
这样你就把对象和p绑定在一起,任何试图转移拥有权的操作都会编译报错,但是务必记住,这里的const不是常指针的意思,不代表所指向的内容不变,代表的是拥有权不能转移。
一旦指定为const,就不能作为返回值,也不能当参数,也不能当做右值。
Const auto_ptr应用举例:
#include<iostream>
#include<memory>
using namespace std;
int main()
{
const auto_ptr<int> p(new int(42));
const auto_ptr<int> q(new int(0));
const auto_ptr<int> r;
cout<<"after initalization:"<<endl;
cout<<"P: "<<*p<<endl;
cout<<"q: "<<*q<<endl;
//cout<<"r: "<<*r<<endl; error
*q=*p;
// *r=*q; error:
*p=77;
cout<<"after assigning:"<<endl;
cout<<"p: "<<*p<<endl;
cout<<"q: "<<*q<<endl;
//cout<<"r: "<<*r<<endl; error
// q=p; error: const auto_ptr
// r=p; error: const auto_ptr
system("pause");
return 0;
}
这个程序也非常简单,注释的那几个地方都是错误的,大家也可以看到,const型的auto_ptr并不是所指的内容不变,可以进行赋值,是指针之间无法相互赋值而已。
Auto_ptr的错误用法
1:auto_ptr之间不能共享拥有权。
2:并不存在针对数组而设计的auto_ptr。
3:auto_ptr不是一个四海通用的指针,使用务必注意。
4:auto_ptr不能满足容器对元素的要求,容器要求元素最起码可以复制和赋值,但是auto_ptr一旦赋值就会转移拥有权,所以容器不能用来装auto_ptr。
关于auto_ptr的例子,这里我就不举了,书上很大一堆,可以自己看看,本节的主要内容就掌握智能指针的定义,初始化以及拥有权的问题,比较基础,简单。
我还需要说一下,智能指针虽然是一个很好的设计,但是我希望初学者谨慎使用auto_ptr,因为没有全面的掌握它的方方面面,轻易使用往往会造成严重的后果,因为精力有限也不能分析到很多,比如智能指针的计数问题,作为类成员时重写默认函数问题,要全面的掌握它,推荐《STL编码剖析》《C++标准程序库》。