现在的位置: 首页 > 综合 > 正文

c\c++复习基础要点10—智能指针

2018年02月21日 ⁄ 综合 ⁄ 共 3920字 ⁄ 字号 评论关闭

C++标准库提供的auto_ptr是一种智能指针,帮助程序员防止“被异常抛出时发生资源泄露”。

 

auto_ptr的设计动机:

1.       获得一些资源

2.       执行一些动作

3.       释放获取的资源

如果一开始获取的资源被绑定于局部对象身上,当函数退出时,它们的析构函数被调用,从而自动释放这些资源,然而事情并不是总是如此顺利,如果资源是以显式手法获得,而且没有绑定在任何对象身上,那么必须显式手法释放。这样情形常常发生在指针身上。

 

例子:

void  f()

{

     ClassA * ptr = new ClassA;

     ……..

     delete ptr;

}

 

如果我们忘记了释放资源或者是异常退出没有释放资源,这样就会给程序带来灾难。

 

如果使用智能指针,情况就会大不相同。这个智能指针应该保证,无论在何种情况下,只有自己被摧毁,就一定连带释放其所指的资源。

 

auto_ptr是这样一种指针:它是“它所指向的对象”的拥有者(owner)。所以,当身为对象拥有者的auto_ptr被摧毁时,该对象也将遭到摧毁。auto_ptr要求一个对象只能有一个拥有者,严禁一物二主。

 

例子:上面的例子改写为使用智能指针

 

#include<memory>   //auto_ptr 包含在文件memory中

 

void  f()

{

      std::auto_ptr<ClassA> ptr(new  ClassA);

      …….

}

 

这样就不用担心忘记释放new的ClassA或异常退出了

 

注意:auto_ptr不允许你使用一般指针的惯用的赋值初始化方式。你必须直接使用数值来初始化:

 

例子:

std::auto_ptr<ClassA>ptr1(new ClassA);    //ok

std::auto_ptr<ClassA>ptr2 = new ClassA;   //ERROR

 

 

auto_ptr拥有权的转移:

   由于一个auto_ptr会删除其所指对象,所以这个对象绝对不能同时被其它对象“拥有”。绝对不应该出现多个auto_ptr同时拥有一个对象的情况。

 

拥有权移交:auto_ptr的拷贝构造函数和赋值运算符会将对象拥有权移交出去。

 

例子:

std::auto_ptr<ClassA>ptr1(new ClassA);

std::auto_ptr<ClassA>  ptr2(ptr1);

 

在第一个语句中,ptr1拥有了new出来的对象。在第二个语句中,拥有权由ptr1转交给ptr2。此后ptr2就拥有了那个new出来的对象,而ptr1不再拥有它。这样,对象就只会被delete一次——在ptr2被销毁的时候。

 

例子:赋值操作

std::auto_ptr<ClassA>ptr1(new classA);

std::auto_ptr<ClassA>ptr2;

ptr2=ptr1;

 

在这里,赋值操作将拥有权从ptr1转移至ptr2。

如果ptr2赋值之前正拥有另一个对象,则赋值操作发生时会调用delete,将该对象释放,再拥有ptr1指向的对象的拥有权。

 

例子:

std::auto_ptr<ClassA>ptr1(new ClassA);

std::auto_ptr<ClassA>ptr2(new ClassA);

 

ptr2=ptr1;

 

 

起点和终点:

 

拥有权的移转,使得auto_ptr产生一种特殊用法:某个函数可以利用auto_ptr将拥有权移交给另一个函数。

这种事情可能在两种情形下出现:

1.       某函数是数据的终点。如果auto_ptr以传值(by value)方式被当做一个参数传递给某函数。此时被调用端的参数获得了这个auto_ptr的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被释放。

void  sink(std::auto_ptr<ClassA>);

2.       某函数是数据的起点。当一个auto_ptr被返回,其拥有权便被转交给调用端了。

 

例子:

std::auto_ptr<ClassA>f()

{

     std::auto_ptr<ClassA> ptr(newClassA);

     …….

     return ptr;

}

 

void g()

{

     std::auto_ptr<ClassA>p;

     for(int i=0;i<10;i++)

            p=f();

}

 

 

 

问题:当智能指针以传值的方式传给函数做参数,但是只是想打印指针指向的值,而函数并不是指针的终点。

 

void bad_print(std::auto_ptr<T>  p)

{

     if(p.get()==NULL)

     {

         std::cout<< “NULL”;

     }

     else

     {

         std::cout<<*p;

     }

}

 

 

std::auto_ptr<int>p(new int);

*p=42;

bad_print(p);

*p=18;          //error  p所指的对象已经被销毁

 

 

你可能会认为,将auto_ptr以传引用方式传递就万事大吉了。但是以引用传递智能指针你根本无法预知拥有权是否移交,这是非常糟糕的设计,应该全力避免。

 

解决:可以使用constant reference ,你无法变更任何constant reference的拥有权:

 

const  std::auto_ptr<int> p(new int);

*p=42;

bad_print(p);

*p=18;        //ok

 

 

总而言之,常数auto_ptr减小了“不经意转移拥有权”所带来的危险。只要一个对象通过auto_ptr传递,就可以使用常数型auto_ptr来终结拥有权转移,此后拥有权将不能再进行转移。

 

在这里,关键词const并非意味你不能更改auto_ptr所拥有的对象,而是意味你不能更改auto_ptr的拥有权。

 

例子:

 

const  std::auto_ptr<int>  p(new int);

std::auto_ptr<int>  q(new int);

 

*p=42;

bad_print(p);

*p=*q;

p=p;     //error

 

 

auto_ptr作为类成员之一

 

在class中使用auto_ptr,你可以因此避免遗失资源。如果你为auto_ptr而非一般指针作为成员,当对象被删除时,auto_ptr会自动删除其所指的成员对象,于是你也就不再需要析构函数了。此外,即使在初始化期间异常抛出,auto_ptr也可以帮助避免资源遗失。

注意:只有对象被完整构造成功,才有可能于将来调用其析构函数。

 

尽管你可以略过析构函数,却还是不得不自己写拷贝构造函数和赋值操作符。缺省情况下,这两个操作都会移交拥有权,这恐怕并非你所愿。为了避免拥有权的意外移交,如果你的auto_ptr在整个生命期间内都不必改变其所指对象的拥有权,你可以使用const auto_ptr。

 

 

auto_ptr的错误运用:

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

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

auto_ptr不可以指向数组,因为auto_ptr是透过delete而非delete[]来释放其所拥有的对象。

3.       auto_ptr不满足STL容器对其元素的要求

因此请绝对不要将auto_ptr作为标准容器的元素。

auto_ptr实例:

 

#include<iostream>

#include<memory>

using  namespace std;

 

template<classT>

ostream&operator<< (ostream & strm,const auto_ptr<T> & p)

{

      if(p.get()==NULL)

            strm<< “NULL”

      else

            strm<<*p;

     return strm;

 

}

 

int  main()

{

     auto_ptr<int> p(new  int(42));

     auto_ptr<int> q;

    

     cout<< “after  initialization:”<<endl;

     cout<< “p:”<<p<<endl;

     cout<< “q:”<<q<<endl;

 

 q=p;

 cout<< “after  assigning auto pointers:”<<endl;

 cout<< “p:”<<p<<endl;

     cout<< “q:”<<q<<endl;

 

     *q+=13;

     p=q;

     cout<< “after change andreassignament:”<<endl;

     cout<< “p:”<<p<<endl;

     cout<< “q:”<<q<<endl;

 

 

}

 

程序输出:

after  initialization:

p: 42

q: NULL

after  assigning auto pointers:

p: NULL

q: 42

after changeand reassignament:

p: 55

q: NULL

 

 

注:

auto_ptr::element_type

auto_ptr所拥有对象的型别

 

auto_ptr::get()

返回auto_ptr所指对象的地址

如果auto_ptr未指向任何对象,返回null指针

 

T *auto_ptr::release()

放弃auto_ptr原先所拥有的对象的拥有权

返回auto_ptr原先拥有对象的地址

如果auto_ptr原先未拥有任何对象,返回null指针

 

 

 

抱歉!评论已关闭.