C++基础之auto_ptr

【auto_ptr 的由来】   

先看下面的代码。

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif

void ABC()
{
	throw "Exception!";
}

void Func()
{
	int* pi = new int(31);
	ABC(); //如果ABC() 抛出异常,delete得不到执行,内存泄露
	delete pi;
}

int _tmain(int argc, _TCHAR* argv[])
{
	_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);  
	
	try
	{
		Func();
	}
	catch(...)
	{
	}

	return 0;
}


    动态分配了资源pi,如果在ABC()函数的执行中出现异常,控制跳转到main的catch中,执行不到delete语句,造成资源泄漏。

Detected memory leaks!

Dumping objects ->

d:\workspace\myprojects2010\smartpointer\smartpointer.cpp(20) : {105} normal block at 0x00348120, 4 bytes long.

Data: < > 1F 00 00 00

Object dump complete.

    auto_ptr 是C++标准库提供的一种智能指针(模板类),主要用来解决此类问题,使动态内存和异常之间更加友好。Auto_ptr 保证当异常抛出时,分配的对象能自动销毁。用auto_ptr将 int 包装一下,如果ABC()抛出异常,pi会被自动销毁(pi 其实是一个局部对象,其析构函数中会释放它绑定的动态分配对象,析构函数执行的时机,是在控制权被转移到catch之前)。

void Func()
{
	//No suitable constructor exsits to convert int* to auto_ptr<int>
	//auto_ptr<int> pError = new int; 
	auto_ptr<int> pi(new int);
	*pi = 31;
	ABC();
}

注意此处pi不是一个裸指针,而是一个auto_ptr的对象,它重载了操作符&, *, -> 所以能够像使用指针一样去使用它。

在实例化对象pi时,只能使用括号的方式,不能使用 = 。(有的IDE可能允许这种方式,但是运行会出错)下文有解释。

【auto_ptr 详解】

1. auto_ptr 在构造时,获取对某个对象的所有权(ownership),在析构时释放其拥有的那个对象。一个萝卜一个坑,两个auto_ptr不能同时拥有同一个对象。

int* p = new int(1);

auto_ptr<int> ap1(p);

auto_ptr<int> ap2(p);

 

ap1和ap2都认为p是归它管的,析构时都试图delete p,两次删除同一个对象在C++标准中是未定义的,在VS2010中运行会出错。

2. 不能用auto_ptr来管理数组指针。因为析构函数中释放资源使用的是delete,而释放一个数组需要delete []。

int* pa = new int[10];

pa[0] = 0;

auto_ptr<int> ap(pa);

(ap.get())[1] = 1;

 

3. 构造函数的explicit关键字,阻止从一个裸指针隐式转换成auto_ptr类型。

这种形式是错误的!

int* p = new int(2);

auto_ptr<int> pa = p;

 

补充:explicit 关键字的用法。

class People
{
public:
	explicit People(int a)
	{
		m_age = a;
	}
private:
	int m_age;

};

用三种方式创建People的实例

	People p1(10); //类变量声明方式
	People* p2 = new People(20); //People类的指针变量
	//People p3 = 30;

第三种方式进行了一次隐式类型转换,编译器自动将对应于构造函数参数类型的数据转换为了该类的对象,添加了explicit关键字后,这种方式被禁止。

4. auto_ptr 不是引用计数型的智能指针,它对于裸指针有完全的占有性。拷贝或赋值的源对象将失去对裸指针的所有权,变成空的,目标对象将先释放原来拥有的对象,再接管新的裸指针。

 

这种情况比较隐蔽的情形是将auto_ptr作为函数参数按值传递。在函数的作用域内会产生一个局部对象来接收传入的auto_ptr(拷贝构造),这样,传入的实参就失去了对原指针的所有权,而局部对象在函数退出时会delete掉这个指针。

void f(auto_ptr<int> ap)
{
	cout << *ap;
}

 

	auto_ptr<int> ap1(new int(5));
	f(ap1);

ap1变成empty了。最好避免将auto_ptr 作为函数参数按值传递。

5. auto_ptr 不具有值语义,所以不能被用在stl标准容器中。

值语义是指符合以下条件的类型,假设有类A

A a1;

A a2(a1);

A a3;

a3 = a1;

那么 a2 == a1, a3 == a1。显然auto_ptr不符合。而STL标准容器中需要用到大量的拷贝复制操作,操作的类型必须符合这个条件。

6. 辅助函数

get():返回auto_ptr 拥有的对象指针。

release():返回拥有的指针,将拥有的裸指针置为null。不会释放对象,仅归还所有权。

reset():接收所有权。如果接收所有权的auto_ptr已经拥有某对象,需先释放。如不带参数,释放内部管理的内存。

 

【参考】

http://www.cppblog.com/SmartPtr/archive/2007/07/05/27549.html

你可能感兴趣的:(C++,exception,report,delete,leak,Constructor)