您的位置:首页 > 编程语言 > C语言/C++

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

2013-10-03 23:02 405 查看
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指针
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: