构造函数应该尽量避免产生异常
1.说明
构造函数是对一个对象进行初始化,一般来说,构造函数不应该设计的太复杂,即只进行简单的成员变量的初始化即可,这样就可以更好的保证构造函数不会产生异常。
2.构造函数产生异常的危害
C++在进行实例化对象,当调用构造函数的过程中发生异常时,这将导致无法调用对象的析构函数,这时仅仅清理和释放产生异常前的那些C++管理的变量空间等,而我们动态申请的资源(通过析构函数释放)是无法自动释放的,这将会导致资源泄露。
3.解决构造函数出现的异常
如果你的构造函数,不仅仅是进行简单的成员变量的初始化,还需涉及到了一系列资源的分配,可以通过下述方法解决:容易出现资源泄露代码如下:
class Test
{
public:
Test()
{
/*
pA = new A(), 注意了,这里针对的是动态分配资源,失败返回NULL,进行DELETE NULL不会出现异常,如果是其他资源,这里出现异常需要额外的处理,
即需要考虑资源失败和成功的不同处理方式,例如,对互斥器未加锁的情况下进行解锁,会导致未定义行为。
*/
pA = new A();
...
}
~Test()
{
delete pA;
...
}
private:
A* pA;
};
(1)、方法一:通过异常处理机制,一旦发生异常,先进行资源的释放
Test::Test()
{
try
{
pA = new A(); //资源的异常,资源申请失败也就谈不上释放资源了,更主要考虑的是,申请资源成功后的下面的异常。
...
}
catch (...)
{
if(NULL != pA)
{
delete pA;
}
throw; //抛出对应异常,交给上层处理
}
}
(2)、方法二:通过智能指针进行管理
class Test
{
public:
Test()
{
spA = std::make_shared<A>; //资源由智能指针进行处理
...
}
~Test()
{
}
private:
std::shared_ptr< A > spA;
};
(3)、方法三:把资源申请和释放的操作存放在另外的Init()和Release()函数
class Test
{
public:
Test()
{
pA = NULL;
if (!Init())
{
Release();
}
...
}
~Test()
{
Release();
}
private:
bool Init()
{
try
{
pA = new A(); //资源的异常,资源申请失败也就谈不上释放资源了,更主要考虑的是,申请资源成功后的下面的异常。
...
}
catch (...)
{
return false;
}
}
return true;
}
void Release()
{
if(NULL != pA)
{
delete pA;
pA = NULL;
}
}
private:
A* pA;
};
总结:
(1)、构造函数中发生异常,会导致析构函数不能被调用,但对象本身已申请到的内存资源会被系统释放(已申请到资源的内部成员变量会被系统依次逆序调用其析构函数)。
(2)、由于析构函数不能被调用,这就可能会造成内存泄露或系统资源未被释放。
(3)、构造函数中如果发生异常,必须在构造函数抛出异常之前,把系统资源释放掉,以防止内存泄露。