C++学习之auto_ptr智能指针

auto_ptr的设计动机

函数的操作经常是依以下模式进行:

1.获取一些资源。

2.执行一些操作。

3.释放所获取的资源。

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

如下一个运用new和delete来产生和销毁对象的典型例子:

void f() 
{ 
ClassA* ptr = new ClassA;  //create an object explicitly
...  //perform some operations
delete ptr;  //clean up (destroy the object explicitly)
} 
如果在delete对象之前,函数就返回了或出现异常而退出,那么函数就根本不会调用delete语句。这很可能造成资源泄露,而防止这种资源泄露的常见办法就是捕获所有异常,但这会使程序代码变得非常复杂和累赘。

如果使用智能指针,情形就会大不一样。这个智能指针应该保证,无论在何种情形下,只要自己被摧毁,就一定连带释放其所指资源。而由于智能指针本身就是区域变量,所以无论是正常退出,还是异常退出,它一定会被销毁。auto_ptr正是这种指针。

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

#include <memory> 
void f() 
{ 
//create and initialize anauto_ptr 
std::auto_ptr<ClassA> ptr(new ClassA); 
...  //perform some operations
}
通过使用智能指针,不再需要delete,也不需要捕获异常。不过注意: 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同时拥有一个对象的情况。

这个条件会使auto_ptr的copy构造函数和assignment操作符的实现有所不同:copy构造函数和assignment操作符会将对象拥有权交出去。

//initialize anauto_ptr with a new object
std::auto_ptr<ClassA> ptr1(new ClassA); 
//copy theauto_ptr 
//- transfers ownership fromptr1 toptr2 
std::auto_ptr<ClassA> ptr2(ptr1); 
此时ptr2指向原ptr1所指对象,而ptr1现在指向NULL。而且要注意:只有auto_ptr可以拿来当做另一个auto_ptr的初值,普通指针是不行的:

std::auto_ptr<ClassA> ptr;  		  //create an
auto_ptr 
ptr = new ClassA;  			  //ERROR
ptr = std::auto_ptr<ClassA>(new ClassA);  //OK, delete old object
					  // and own new
拥有权的转移,使得auto_ptr产生一种特殊用法:某个函数可以利用auto_ptr将拥有权转交给另一个函数。这种事情可能发生在两种情形下:

  1. 某函数是数据的终点。如果auto_ptr以传值方式被当做一个参数传递给某函数,就有这种情况。此时被调用端的参数获得这个auto_ptr的拥有权,如果函数不再将它传递出去,它所指的对象就会在函数退出时被删除。
  2. 某函数是数据的起点。当一个auto_ptr被返回,其拥有权便被转交给调用者了。
auto_ptr的语义本身就包含了拥有权,所以如果你无意转交你的拥有权,就不要在参数列中使用auto_ptr,也不要以它作为返回值。不过你可以运用constant reference,向函数传递拥有权。 这里的constant并非意味着你不能更改auto_ptr所拥有的对象,而是你不能更改auto_ptr的拥有权。

auto_ptr作为成员之一
如果你以auto_ptr而非一般指针作为成员,当对象被删除时,auto_ptr会自动删除其所指的成员对象,于是你也就不再需要析构函数了。此外,即使在初始化期间抛出异常,auto_ptr也可以帮助避免资源泄漏。注意,只有当对象被完全构造成功,才有可能于将来调用其析构函数。这就造成了资源泄漏的隐患:如果第一个new成功了,第二个new却失败了,就会造成资源泄漏。例如:

class ClassB { 
private: 
ClassA* ptr1;  //pointer members
ClassA* ptr2; 
public: 
//constructor that initializes the pointers
//- will cause resource leak if secondnew throws
ClassB (ClassA val1, ClassA val2) 
: ptr1(new ClassA(val1)), ptr2(new ClassA(val2)) { 
} 
//copy constructor
//- might cause resource leak if second new throws
ClassB (const ClassB& x) 
: ptr1(new ClassA(*x.ptr1)), ptr2(new ClassA(*x.ptr2)) { 
} 
//assignment operator
const ClassB& operator= (const ClassB& x) { 
*ptr1 = *x.ptr1; 
*ptr2 = *x.ptr2; 
return *this; 
} 
~ClassB () { 
delete ptr1; 
delete ptr2; 
} 
... 
}; 
使用auto_ptr,你可以轻松避免这场悲剧:

class ClassB { 
private: 
const std::auto_ptr<ClassA> ptr1;  //auto_ptr members
const std::auto_ptr<ClassA> ptr2; 
public: 
//constructor that initializes theauto_ptrs
//- no resource leak possible
ClassB (ClassA val1, ClassA val2) 
: ptr1 (new ClassA(val1)), ptr2(new ClassA(val2)) { 
} 
//copy constructor
//- no resource leak possible
ClassB (const ClassB& x) 
: ptr1(new ClassA(*x.ptr1), ptr2(new ClassA(*x.ptr2)) { 
} 
//assignment operator
const ClassB& operator= (const ClassB& x) { 
*ptr1 = *x.ptr1; 
*ptr2 = *x.ptr2; 
return *this; 
} 
//no destructor necessary
//(default destructor letsptr1 andptr2 delete their objects)
... 
}; 
auto_ptr的错误运用

(1)auto_ptr之间不能共享使用权

一个auto_ptr千万不能指向另一个auto_ptr(或其他对象)所拥有的对象。否则,当第一个指针删除该对象后,另一个指针突然间指向了一个已被销毁的对象,那么,如果再使用那个指针进行读写操作,就会引发一场灾难。

(2)并不存在针对array而设计的auto_ptr

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

(3)auto_ptr绝非一个“四海通用”的智能指针

并非任何使用智能指针的地方,都适用auto_ptr。特别注意,它不是引用计数型指针——这种指针保证,如果有一组智能型指针指向一个对象,那么当且仅当最后一个智能指针被销毁时,该对象才会被销毁。

(4)auto_ptr不满足STL容器对其元素的要求

auto_ptr并不满足STL容器对于元素的最基本要求,因为在拷贝和赋值动作之后,原本的auto_ptr和新产生的auto_ptr并不相等。

auto_ptr类别的实作示范

// util/autoptr.hpp
/* classauto_ptr 
*- improved standard conforming implementation 
*/
namespace std { 
//auxiliary type to enable copies and assignments (now global)
template<class Y> 
struct auto_ptr_ref { 
Y* yp; 
auto_ptr_ref (Y* rhs) 
: yp(rhs) { 
} 
}; 
template<class T> 
class auto_ptr { 
private: 
T* ap;  //refers to the actual owned object (if any)
public: 
typedef T element_type; 
//constructor
explicit auto_ptr (T* ptr = 0) throw() 
: ap(ptr) { 
} 
//copy constructors (with implicit conversion)
//- note: nonconstant parameter
auto_ptr (auto_ptr& rhs) throw() 
: ap (rhs. release()) { 
} 
template<class Y> 
auto_ptr (auto_ptr<Y>& rhs) throw() 
: ap(rhs.release()) { 
} 
//assignments (with implicit conversion)
//- note: nonconstant parameter
auto_ptr& operator= (auto_ptr& rhs) throw() { 
reset(rhs.release()); 
return *this; 
} 
template<class Y> 
auto_ptr& operator= (auto_ptr<Y>& rhs) throw() { 
reset(rhs.release()); 
return *this; 
} 
//destructor
~auto_ptr() throw() { 
delete ap; 
} 
//value access
T* get() const throw() { 
return ap; 
} 
T& operator*() const throw() { 
return *ap; 
} 
T* operator->() const throw() { 
return ap; 
} 
//release ownership
T* release() throw() { 
T* tmp(ap); 
ap = 0; 
return tmp; 
} 
//reset value 
void reset (T* ptr=0) throw(){ 
if (ap != ptr) { 
delete ap; 
ap = ptr; 
} 
} 
/* special conversions with auxiliary type to enable copies 
and assignments
*/
auto_ptr(auto_ptr_ref<T> rhs) throw() 
: ap(rhs.yp) { 
} 
auto_ptr& operator= (auto_ptr_ref<T> rhs) throw() { //new
reset(r.yp); 
return *this; 
} 
template<class Y> 
operator auto_ptr_ref<Y>() throw() { 
return auto_ptr_ref<Y>(release()); 
} 
template<class Y> 
operator auto_ptr<Y>() throw() { 
return auto_ptr<Y>(release()); 
} 
}; 
} 






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值