首先说说标题可能取得有些大,但是可以理解为编程过程中有效的防止写的代码中有内存泄漏。好了废话不多说了,首先看下面一段代码。
class Image
{
public:
Image(const std::string& imgFileName);
...
}
class Voice
{
public:
Voice(const std::string& vFileName);
...
}
class People
{
public:
People(const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName);
~People( );
private:
string name;
int age;
int height;
Image *pImg; //图像
Voice *pVoi; //声音
};
People::People( const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName ):name(n),age(a),height(h),pImg(0),pVoi(0)
{
if(imgFileName != "")
{
pImg = new Image( imgFileName ) ;
}
if(vFileName != " ")
{
pVoi = new Voice ( vFileName );
}
}
People::~People( )
{
delete pImg;
delete pVoi;
} |
上面代码粗略看似没有问题,但是有没有想到如果People构造函数出错(内存不足,无法分配内存)怎么办?其结果可以预见,就是一个异常抛出来。但是我们仔细想想此时如果已经构造了Image类对象,而在构造Voice类对象时抛出的错误,这个情况会怎么办?程序会因为异常而停止,后面代码不会执行,那么pImg指针所指向的内存就不会得到正确的释放,那么内存就泄漏了。情况如下面代码:
...
People *p =NULL;
try{
p=new People("test",20,170,".../images/image01.jpg","../voices/voice01.dat");
...
catch( ... ){
delete p;
throw;
}
delete p;
} |
仔细想想 new People("test",20,170,".../images/image01.jpg","../voices/voice01.dat")里,如果最后为Image分配的内存被丢失,因为new操作没有成功完成,程序不会p进行赋值操作。所以catch中是没有任何操作的,已被分配的内存就丢失了。
因为对象在构造中抛出异常后C++不负责清除对象,所以我们需要重新设计构造函数让它们在运到异常的时候自己能清除所占用的内存。
People::People( const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName ):name(n),age(a),height(h),pImg(0),pVoi(0)
{
try {
if(imgFileName != "")
{
pImg = new Image( imgFileName ) ;
}
if(vFileName != " ")
{
pVoi = new Voice ( vFileName );
}
}
catch( ...) {
delete pImg ;
delete pVoi;
throw ;
} |
这样就行了,解决上面的情况。让成员变量成为const指针,这样设计也合理,避免指针无意被改变。
Image * const pImg; //图像
Voice * const pVoi; //声音 |
那么这样就只能用成员初始化列表为指针初始化,就没有其他地方可以给const指针赋值了。
People::People( const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName ):name(n),age(a),height(h),
pImg( imgFileName !="" ? new Image( imgFileName ) : 0 ),
pVoi( vFileName != "" ? new Voice( vFileName ) : 0)
{} |
如果这样就重新回到上面所遇到的问题,即构造过程中抛出异常,指针可能无法正确的释放所占内存。那么我们可以进一步对代码进行改进,如下:
People::People( const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName ):name(n),age(a),height(h),
pImg( initImage( imgFileName ) ),
pVoi( initVoice( vFileName ) )
{}
Image* People::initImage(const string& imgFileName)
{
if(imgFileName !="") return new Image(imgFileName);
else return 0;
}
Voice* People::initVoice(const string& vFileName)
{
try
{
if(vFileName !="")return new Voice(vFileName)
esle return 0;
}
catch(... )
{
delete pImg;
throw;
}
} |
这样在调用构建Voice对象中加入try...catch...用于释放pImg所占用的内存空间。其实有一个比其更简单的方法就是使用智能指针。
const auto_ptr<Image> pImg;
const auto_ptr<Voice> pVoi;
People::People( const std::string& n,const int& a,const int& h,const std::stirng& imgFileName,
const std::string& vFileName ):name(n),age(a),height(h),
pImg( imgFileName !="" ? new Image( imgFileName ) : 0 ),
pVoi( vFileName != "" ? new Voice( vFileName ) : 0)
{} |
那么问题就算解决了,因为当其中有一个创建失败,离开函数的时候,智能指针会自动删除已经创建的空间,防止内存泄漏了。 |