auto_ptr智能指针

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的智能指针才能相互赋值,常规指针不能(一个是对象,一个是指针,很好理解吧)

2auto_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把它对该对象的拥有权交给了ptr2ptr1不再拥有该对象拥有权

这样的话,该对象只会被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;

}

 

auto_ptr智能指针_第1张图片

 

这个程序也非常简单,注释的那几个地方都是错误的,大家也可以看到,const型的auto_ptr并不是所指的内容不变,可以进行赋值,是指针之间无法相互赋值而已。

Auto_ptr的错误用法

1auto_ptr之间不能共享拥有权。

2:并不存在针对数组而设计的auto_ptr

3auto_ptr不是一个四海通用的指针,使用务必注意。

4auto_ptr不能满足容器对元素的要求,容器要求元素最起码可以复制和赋值,但是auto_ptr一旦赋值就会转移拥有权,所以容器不能用来装auto_ptr

关于auto_ptr的例子,这里我就不举了,书上很大一堆,可以自己看看,本节的主要内容就掌握智能指针的定义,初始化以及拥有权的问题,比较基础,简单。

我还需要说一下,智能指针虽然是一个很好的设计,但是我希望初学者谨慎使用auto_ptr,因为没有全面的掌握它的方方面面,轻易使用往往会造成严重的后果,因为精力有限也不能分析到很多,比如智能指针的计数问题,作为类成员时重写默认函数问题,要全面的掌握它,推荐《STL编码剖析》《C++标准程序库》。

 

你可能感兴趣的:(STL)