more-effective-c++ 序列2 异常(第10节,通过智能指针解决构造函数的堆内存资源泄露)的测试示例

声明:

在看这一节之前,希望你已经阅读过上一节展示的一个资源泄露的例子程序。只有当我们发现问题的所在,再看给出的解决方案的时候,我们才会发现它的美好。这个我是深有体会,如果平平常常的拿出这个例子,也许看不出它的完美,然而如果去探索其这样做的原因,追本溯源,去探索它一步步发展的过程,我们才真的会发现它的伟大。你不禁会惊叹一声,智能指针,真智能。


示例代码:

通过智能指针的使用,消除了上节会出现Image堆内存泄露的情况。使用很简单,思想很精髓。

#include <iostream>
#include <time.h>
#include <memory>
using namespace std;


/*
作者:lpstudy
日期:2013-3-16
内容:more effective C++异常一章第10节,在构造函数中防止堆内存资源泄露,使用智能指针完成
*/
#define TRACE_FUCTION_AND_LINE(fmt, ...) printf("[%30s:%4d] "fmt"\n",__FUNCTION__, __LINE__, ##__VA_ARGS__)




class Image
{
public:
	Image(const string& strImgName)
		:m_strImgName(strImgName)
	{
		TRACE_FUCTION_AND_LINE();
	}
	~Image(){TRACE_FUCTION_AND_LINE();}
private:
	const string& m_strImgName;
};




/*
AudioClip类是在image类构造完成之后才进行构造,下面的AudioClip抛出异常可用来观察image的析构函数实际上由于异常并没有被调用
导致image内存泄露。
*/
class AudioClip
{
public:
	AudioClip(const string& strAudioName)
		:m_strAudioName(strAudioName)
	{
		TRACE_FUCTION_AND_LINE();
		throw 1;//BookEntry抛出异常,导致Image析构没有被调用
	}
	~AudioClip(){TRACE_FUCTION_AND_LINE();}
private:
	const string& m_strAudioName;
};


/*
BookEntry构造函数负责初始化Image和AudioClip,这样如果Image和AudioClip的构造函数
出现异常的话,BookEntry构造函数会立刻返回,这样构造的对象就不是一个完整的对象。
*/
class BookEntry
{
public:
	BookEntry(const string& name, 
			  const string& address = "",
			  const string& imageFileName = "",
			  const string& audioclipName = "")
	: m_strName(name),
	  m_strAddress(address),
	  m_pImage((imageFileName.empty()) ? 0 : new Image(imageFileName)),
	  m_pAudioClip((audioclipName.empty()) ? 0 : new AudioClip(audioclipName))
	{
		TRACE_FUCTION_AND_LINE();


	}
	~BookEntry()
	{
		//there is no need to call this delete, auto_ptr will do this for us perfectly!!!
		//delete m_pImage;
		//delete m_pAudioClip;
		TRACE_FUCTION_AND_LINE();
	}
	void Log () {TRACE_FUCTION_AND_LINE("My Log------"); throw 1;}
 
private:
	string m_strName;
	string m_strAddress;
	auto_ptr<Image> m_pImage;
	auto_ptr<AudioClip> m_pAudioClip;
};


/*
测试构造函数异常的时候,由于auto_ptr的保护,虽然audioclip出现了异常
Image的内存还是被正常释放。
*/
void TestStackMemory()
{
	try{
		TRACE_FUCTION_AND_LINE("");
		BookEntry bookEntry("lpstudy", "beijing", "imageFileName", "audioClipName");
	}
	catch(int e)
	{
		TRACE_FUCTION_AND_LINE("exception int = %d", e);
	}
}
/*
测试堆内存的时候,构造函数异常的时候
可以看出这个时候pBookEntry还没有完全构造出来,返回的指针实际上是NULL
但是由于auto_ptr内存的保护,Image的内存被正确的释放。
*/
void TestHeapMemory()
{
	BookEntry *pBookEntry = 0;
	try{
		TRACE_FUCTION_AND_LINE("");
		pBookEntry = new BookEntry("lpstudy", "beijing", "imageFileName", "audioClipName");	
	}
	catch(int e)
	{
		TRACE_FUCTION_AND_LINE("exception int = %d", e);
		TRACE_FUCTION_AND_LINE("pBookEntry = %08p", pBookEntry);
		delete pBookEntry;//实际上delete null, 没有任何意义
	}
}
int main()
{
	TRACE_FUCTION_AND_LINE("Trace BookEntry.......");
	TestStackMemory();
	TestHeapMemory();


	/*
	const auto_ptr与auto_ptr的区别
	const auto_ptr不可以改变其维护的指针本身,但是可以改变其维护的指针的值。
	类似于:char* const p = "p100";
	于是针对于上面的Image对象,即使它的指针需要是const类型的,通过使用const auto_ptr<Image>即可
	*/
	string *p1 = new string("p100");
	string *p2 = new string("p200");
	const auto_ptr<string> pAutoptrString(p1);
	const auto_ptr<string> pAutoptrString2(p2);
	//pAutoptrString.reset(p2);//reset是non-const成员函数,不可以被const对象调用。non-const对象既可以调用const成员函数,也可以调用non-const成员函数
	pAutoptrString.get()->assign("another p100");
	TRACE_FUCTION_AND_LINE("p1=%s, p2=%s", pAutoptrString.get()->c_str(), pAutoptrString2.get()->c_str());
	return 0;
}
/*
采用智能指针auto_ptr,不管const还是non-const指针都可以有同样的处理结果,代码简单清晰,有很大的实用价值。
*/


你可能感兴趣的:(more-effective-c++ 序列2 异常(第10节,通过智能指针解决构造函数的堆内存资源泄露)的测试示例)