<1>写出下面代码并分析存在的问题
#include<iostream>
using namespacestd;
class BaseClass
{
public:
BaseClass()
{
}
};
class MyClass:public BaseClass
{
private:
char*ptr;
public:
MyClass();
};
MyClass::MyClass():BaseClass(),ptr(newchar[10])
{
}
int main()
{
MyClass c1;
return 0;
}
假设在MyClass的构造函数中需要处理内存分配错误,也就是new char[10]出错,它会抛出std::bad_alloc异常,那么应该怎么捕获它呢?想必也只能将其移到构造函数体内:
MyClass::MyClass():BaseClass()
{
try
{
ptr=newchar[10];
}
catch(const bad_alloc& ex)
{
}
}
如果BaseClass的构造函数也抛出异常的话,这样还是有问题的。这就是引入function-try-block的原因,通过它我们可以将try/catch的处理代码放在构造函数中初始化列表的周围,也就能够捕获由基类或成员变量构造函数抛出的任何异常了。
#include<iostream>
using namespacestd;
class BaseClass
{
public:
BaseClass()
{
}
};
class MyClass:public BaseClass
{
private:
char*ptr;
public:
MyClass();
};
MyClass::MyClass()
try:BaseClass(),ptr(newchar[10])
{
cout<<"构造函数中"<<endl;
throw"error";
}
catch(const string& ex)
{
cout<<"异常处理中"<<endl;
}
intmain()
{
MyClass c1;
return 0;
}
C++标准规定在构造函数和析构函数中,如果执行到达处理程序的末尾,在function-try-block的处理程序中捕获的异常必须重新抛出! 也就意味着上述代码在运行时,仍会由于有未处理的异常而终止。
【解释】为什么说标准这样规定是必要的呢?因为在捕获一个异常时,对象可能处于无效状态,因此不能允许构造函数成功地完成,否则构造出的对象是不能确保其有效性的。
<构造函数应做如下修改>
MyClass::MyClass()
try:BaseClass(),ptr(newchar[10])
{
cout<<"构造函数中"<<endl;
throw"error";
}
catch(const string& ex)
{
cout<<"异常处理中"<<endl;
//析构函数结尾fuction-try-block会导致已捕获的异常被重新抛出,
//但一个显式的返回语句将防止流程到达异常处理程序的末尾
return 1;
}
<2>构造函数可以抛出异常
<3>C++标准指明析构函数不能、也不应该抛出异常
C++异常处理模型是为C++语言量身设计的,更进一步的说,它实际上也是为C++语言中面向对象而服务的。C++异常处理模型最大的特点和优势就是对C++中的面向对象提供了最强大的无缝支持。
那么如果对象在运行期间出现了异常,C++异常处理模型有责任清除那些由于出现异常所导致的已经失效了的对象(也即对象超出了它原来的作用域),并释放对象原来所分配的资源, 这就是调用这些对象的析构函数来完成释放资源的任务,所以从这个意义上说,析构函数已经变成了异常处理的一部分。
上面的论述C++异常处理模型它其实是有一个前提假设——析构函数中是不应该再有异常抛出的。试想,如果对象出了异常,现在异常处理模块为了维护系统对象数据的一致性,避免资源泄漏,有责任释放这个对象的资源,调用对象的析构函数,可现在假如析构过程又再出现异常,那么请问由谁来保证这个对象的资源释放呢?而且这新出现的异常又由谁来处理呢?不要忘记前面的一个异常目前都还没有处理结束,因此这就陷入了一个矛盾之中,或者说无限的递归嵌套之中。所以C++标准就做出了这种假设,当然这种假设也是完全合理的,在对象的构造过程中,或许由于系统资源有限而致使对象需要的资源无法得到满足,从而导致异常的出现,但析构函数完全是可以做得到避免异常的发生,毕竟你是在释放资源呀!
<4> more effective c++提出两点理由(析构函数不能抛出异常的理由):
1)如果析构函数抛出异常,则异常点之后的程序不会执行,如果析构函数在异常点之后执行了某些必要的动作比如释放某些资源,则这些动作不会执行,会造成诸如资源泄漏的问题。
2)通常异常发生时,c++的机制会调用已经构造对象的析构函数来释放资源,此时若析构函数本身也抛出异常,则前一个异常尚未处理,又有新的异常,会造成程序崩溃的问题。