4.通过引用捕获异常

来源:互联网 发布:经传软件源代码 编辑:程序博客网 时间:2024/05/17 22:53
跟给函数传递参数一样,有三种选择可以让异常传递到catch子句里。分别是通过指针,传值,和引用。
1.通过指针方式捕获异常:
理论上来说这种方法的实现对于throw传递异常到catch子句中来说是效率最高的。因为在传递异常信息时,只有采用通过指针抛出异常的方法才能做到不拷贝对象。
class exception{...} //C++标准库(STL)中的异常层次类
void someFunction()
{
static exception ex;//定义一个异常对象
...
throw &ex;  //抛出一个指向异常的指针
...
}
void doSomething()
{
try{
someFunction(); //抛出一个异常指针
}
catch(exception *ex) //捕获异常exception*
{ //整个过程没有拷贝对象
...
}
}
虽然这样看上去不错,而且也都能运行正常,但是事实却往往不是如此。
因为程序员在抛出自定义异常时必须考虑指针所指向的对象的生存周期问题,也就是当控制权离开抛出指针的函数后,指针所指向的对象还能够继续生存。全局与静态对象都可以保证这一点。但是程序员很容易忽略这个约束。看看下面的代码:
void someFunction()
{
exception ex;//局部对象,函数退出时生存周期结束
...
throw &ex; //抛出异常
}
问题来了,当catch子句接受抛出的指针时,实际上这个对象已经不存在了。
另一种抛出指针的方法是新建一个堆对象(new 一个):
void someFunction()
{
...
throw new exception; //抛出一个指针,指向存在堆中的对象
...
}
这样的做法虽然避免了捕获一个指向已被释放的对象的指针问题。但是另外一个头疼的问题随之而来了,因为catch字句的作者不知道在使用完这个指针后是否需要将它删除。如果是在堆中,那么必须删除,否则会造成内存泄露。如果不是那么绝对不能删除。但是现在我们对它是否是堆中的对象是未知的。因为一些调用者可能会传递全局或静态对象地址,一些人会传递堆中建立的异常对象的地址。而捕获异常的处理处没办法知道。
另外通过指针捕获异常也不符合C++语言本身规范,因为四个标准的异常所抛出的都不是指向对象的指针,你必须通过引用或值来捕获它们。
2.通过值捕获异常
可以解决上述问题,但是异常抛出时系统会对异常对象拷贝两次。另外一个问题是当派生类的异常对象被做为基类异常对象捕获时。派生类行为会被切掉。当要调用它的虚函数时,系统解析后调用的其实是基类对象的函数。
3.通过引用捕获异常
使用引用可以避免上述问题,不存在对象指针删除问题,异常对象只需要拷贝一次,同时支持虚函数多态。
void someFunction()
{
if(a validation 测试失败)
throw Validation_error();
...
}
void doSomething()
{
try{
someFunction();
}
catch(exception& ex)
{
cerr<<ex.what();
...
}
}
原创粉丝点击