首页 > 代码库 > auto_ptr的使用及其源代码

auto_ptr的使用及其源代码

auto_ptr所要做的工作就是获得一个已经被动态分配的对象,并且当对象不再需要时执行自动地清除,避免内存泄露。看下面两段代码:

(1)

void f()

{

   T* pt(new T);

   /*.....其他代码......*/ 

   delete pt;

}

(2)

void f()

{

   auto_ptr<T> pt(new T);

   /*......其他代码......*/

}

(1)中的代码如果从来没有执行delete语句,可能是因为执行delete之前函数就返回了,或者也可能是因为在执行函数的时候出现了异常,那么在函数开头进行的内存分配的对象将不会删除,于是代码中就出现了一个典型的内存泄露。而(2)中无论函数是否是由于正常退出或者抛出异常而没有执行delete语句,在上面的代码中都不会存在T对象的内存泄露问题,因为当堆回卷时,总能调用pt的析构函数。因此内存清理工作也将自动进行。

①所有权

当你对auto_ptr进行拷贝时,对象的所有权将自动地从源auto_ptr传递到目标auto_ptr中;如果目标auto_ptr已经拥有了一个对象,那么这个对象将首先被释放。在拷贝完成之后,只有在目标auto_ptr中才拥有原来在源auto_ptr中的指针,并在需要的时候对这个指针将执行删除操作,而源 auto_ptr将会被设置为空状态,并且不在能访问曾经拥有的对象。例如:

void f()

{

 auto_ptr<T> pt1(new T);

 aut0_ptr<T> pt2;

 pt2 = pt1;//现在,pt2将拥有指针,而pt1则没有

 pt1->func();//错误,这是一个空指针

}

②封装指针数据成员

template<class T>

class C

{

public:/*......*/

protected:/*......*/

private:

   auto_ptr<T> ptr;

}

这样做有两个好处:一是使我们在类C的析构函数中不用进行内存清理工作;二是如果在类中使用的是裸指针数据成员,而不是auto_ptr数据成员,那么你就必须在类中提供自己的析构函数,拷贝构造函数和拷贝赋值运算符函数,因为在这些函数的默认版本中将会进行错误清理。

③auto_ptr与异常安全性

看下面两个函数:

(1)

string f()

{

  string result;

  result = "hello";

  cout << "baby" << endl;//操作一

  return result;//操作二

}

(2)

auto_ptr<string> f()

{

  auto_ptr<string> result = new string;

  *result = "hello";

  cout << "baby" << endl;//操作一

  return result;//操作二,这个操作依赖所有权的转移,而这个过程是不会抛出异常的

}

对于(1)中返回string对象肯定会调用string的拷贝构造函数,如果在拷贝构造函数中出现了失败,那么由于函数f()已经完成了它的所有操作,因此返回的结果肯定会被丢失。这就会导致操作一完成了而操作二没有完成,但是为了保证函数的行为时原子的:即使在函数中抛出了异常,那么这两个操作也是要么都执行,要么都不执行。所以需要用到(2)的方法,在这种方法中没有构造函数的调用,而是依赖于所有权的转移,而这个过程是不会抛出异常的。

④常量auto_ptr的惯用法

常量auto_ptr是永远不会失去所有权的,事实上,在常量auto_ptr上能进行的所有操作智能是调用operator*()来进行解引用操作,或者调用operator->()和调用get()来查询所包含的指针值。这就意味着,如果想表示一个auto_ptr永远都不要失去所有权,那么我们就可以使用常量auto_ptr惯用法。

const auto_ptr<T> pt1 (new T);

auto_ptr<T> pt2(pt1);//

auto_ptr<T> pt3;

pt3 = pt1;//非法的

pt1.release();//非法的

pt1.reset(new T);//非法的

⑤注意事项:

a.auto_ptr的构造函数时显式的,这意味着不存在从指针到auto_ptr对象的隐式类型转换

auto_ptr<double> pd;

double   *p = new double;

pd = p;//不允许

pd = auto_ptr<double>(p);//允许

auto_ptr<double> pauto(p);//允许

b.auto_ptr模板使用的是delete,而不是delete[],因此只能与new一起使用,而不能与new[]一起使用。

auto_ptr<int> pi(new int [200]);//不允许

c.不要把auto_ptr放到标准的容器中,因为auto_ptr并不能完全满足容器的模板参数类型需求定义,auto_ptr的副本并不是等价的。

vector<auto_ptr<T>> v;//不允许

附auto_ptr源代码:

template<class T>
class auto_ptr
{
public:
	explicit auto_ptr(T *p = 0);//显示类型转换
	
	template<class U>
	auo_ptr(auto_ptr<U>& rhs);//以任何兼容的auto_ptr作为一个新的auto_ptr的初值
	
	~auto_ptr();

	template<class U>
	auto_ptr<T>& operator=(auto_ptr<U>& rhs);//以任何兼容的auto_ptr作为赋值动作的右端

	T& operator*() const;
	T* operator->() const;
	T* get() const;//返回原生指针
	T* release();//撤回原生指针的拥有权,并返回其值

	void reset(T *p = 0);//将拥有的指针删除,并承担p的拥有权

private:
	T *pointee;
	template<class U>
	friend class auto_ptr<U>;//让所有的auto_ptr classes都成为另一个auto_ptr的friends
};

template<class T>
inline auto_ptr<T>::auo_ptr(T *p):pointee(p){}

template<class T>
	template<class U>
	inline auto_ptr<T>::auo_ptr(auto_ptr<U> &rhs):pointee(rhs.release()){}

template<class T>
	template<class U>
	inline auto_ptr<T>::operator =(auto_ptr<U> &rhs)
	{
		if(this != &rhs)
			reset(rhs.release());
		return *this;
	}

template<class T>
inline T& auto_ptr<T>::operator *() const
{
	return *pointee;
}

template<class T>
inline T* auto_ptr<T>::operator ->() const
{
	return pointee;
}

template<class T>
inline T* auto_ptr<T>::get() const
{
	return pointee;
}

template<class T>
inline T* auto_ptr<T>::release()
{
	T *oldPointee = pointee;
	pointee = 0;
	return oldPointee;
}

template<class T>
inline void auto_ptr<T>::reset(T *p)
{
	if (pointee != p)
	{
		delete pointee;
		pointee = p;
	}
}