?!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
如果构造函数内发生异常Q已l分配的资源是不会自动释攄Q比?/strong>
class B{
public:
B(){
printf("into B constructor\n");
}
~B(){
printf("into B destructor\n");
}
};
class C{
public:
C(){
printf("into C constructor\n");
throw std::runtime_error(" exception from C constructor");
}
~C(){
printf("into C destructor\n");
}
};
class A{
public:
A(){
printf("into A constructor \n");
}
~A(){
printf("into A destructor \n");
}
};
class D:A{
public:
D():A(), b(NULL),c(NULL) {
printf("into D constructor\n");
b = new B();
c = new C();
}
~D(){
printf("into D destructor\n");
delete b;
delete c;
}
private:
B *b;
C *c;
};
int main(int argc, char **argv){
D d;
return 0;
}
q行l果如下:
into A constructor
into D constructor
into B constructor
into C constructor
terminate called after throwing an instance of 'std::runtime_error'
what(): exception from C constructor
对象c在构造过E中抛出异常Q指针b指向的内存空间不会被释放?/p>
如何释放b的内存呢Q首先我们可以看出c++是不会调用析构函数的Q因为析构函C知道对象已经构造了多少Q哪些资源该释放Q哪些不该释放,当然~译器可以记录这些内容,但是会严重媄响效率。另外在语义上,c++认ؓQ对象的生命周期是构造函数正常结束至析构函数l束之间Q构造不完全的对象是一个没有生命的东西Q是不存在的Q你不能对一个不存在的对象调用析构函数。编译器默认会做的只是释攑֯象d的内存空间。对象b指向的堆内存可以通过使用异常昄释放
D():A(), b(NULL), c(NULL){
printf("into D constructor\n");
try{
b = new B();
c = new C();
}catch(std::runtime_error &e){
printf("into D constructor catch \n");
delete b; b=NULL;
delete c; c=NULL;
}
q行l果如下:
into A constructor
into D constructor
into B constructor
into C constructor
into D constructor catch
into B destructor
into D destructor
into A destructor
b被正帔R放了Q我们再做一下改变,b和c的构造放到初始化列表中做Q将D的构造函数改成下面这P
D::D() : A(),bQnew BQ)Q,cQnew CQ)Q?/p>
{
}
我们l箋使用异常,会不会有?
D() try:A(), b(new B()), c(new C()) {
printf("into D constructor\n");
}catch(std::runtime_error &e){
printf("in D constructor catch: %s \n", e.what());
cleanup();
}
看上去very nice啊,跑一下试试,
into A constructor
into B constructor
into C constructor
into A destructor
in D constructor catch: exception from C constructor
into B destructor
into C destructor
*** glibc detected *** ./a.out: free(): invalid pointer: ***
指针错误!同时我们可以发现在进入catch语句前基cA执行了析构函敎ͼq说明到达catch语句Ӟ已经跛_了构造函数的范围QD和A的成员数据都已经是不可访问的了?/p>
C++Z么要q样做呢Qؓ什么构造函C外的catch语句中无法访问数据成?
首先Q无法知道b和c是否已经初始化了Q删除未初始化指针是非法的?/p>
其次Q我们假讑֏以知道b初始化了Qc没有初始化,我们要删除bQ如果catch中可以访问b和c的话Q我们做个假设)Q改变B的类型,使B包含一个A*成员数据Q考虑下这U情况,
D() try:A(), b(new B(static_cast<A*>(this))), c(new C()) {
printf("into D constructor\n");
}catch(std::runtime_error &e){
printf("in D constructor catch: %s \n", e.what());
cleanup();
}
A已经析构了,b的数据成员A已经不存在,q是很危险的?/p>
c++认ؓQ不是基类q是数据成员构造出现失败,那么整个对象构造都是失败的Q不存在半成品。构造函数块外的catch语句Q上面这U)即没有昄地重新抛出异常,c++也会自动抛出Q一直到最下层zcd象构造处停住。比如以上例子,catch没有昄throwQ在main函数里:
try{
D d;
}catch (std::runtime_error &e){
printf("int main: %s\n", e.what());
}
仍然会捕捉到C构造函数抛出的runtime_error异常?/p>
lg,可以ȝ以下规则
1. 构造函C外的try-catch语句只有一个用途——{换捕捉到的异常对?/strong>
2. 必须在构造函C内分配资源,不要在初始化列表?/strong>
3. 一定要用try-catch语句理资源的话Q在构造函C内?/strong>
4. 使用RAII方式理资源Q这会少M很多脑细胞,像这样子
class D{
auto_ptr<B> b;
auto_ptr<C> c;
}
D::D() : A( )Qb(new B()), c(new C())
{
}