More Effective C++(条款14:明智运用exception specifications)

本文详细介绍了C++中的异常规范(exception specification),包括其作用、限制及如何正确使用。探讨了函数指针传递时的异常规范检查机制,并提供了一些避免非预期异常的策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. Exception specifications作为函数声明的一部分,用于指出(并不能限制)函数可能会抛出的异常函数.C++规定,一个拥有exception specification的函数指针只能被赋予一个有着相同或更为局限的exception specification的函数地址,因而编译器要保证"在函数指针传递之际检验exception specifications".(但visual studio 2013不支持此项要求)

2. 当函数抛出exception specification未列出的异常时,程序会调用标准库terminate函数,terminate函数又调用abort函数终止程序。局部变量不会获得销毁的机会。这种情况很容易发生(C++明定编译器必须允许"调用某个函数而该函数可能违反调用端函数本身的exception specification"):当函数调用一个exception specification限制程度低于自身的函数时:

extern void f1();//可以抛出任何东西
void f2() throw(int);//只抛出类型为int的exception
void f2() throw(int)
{
    ...
    f1();//合法,甚至即使f1可能抛出一个exception,而该exception违反了f2的exception specification
    ...
}

    通常需要采用以下策略防止"非预期expection"的出现:

    1). 对于带类型参数的函数模板,要尽量避免使用exception specifications。因为不同类型对于相同行为的定义不同,抛出的异常也就不同。因而函数模板很难或不可能确定它具现化的函数实体所可能抛出的异常.

    2). "如果A函数内调用了B函数,而B函数无exception specifications,那么A函数本身也不要设定exception specifications".这个比较容易实现,但有一种情况容易被忽略:使用回调(callback)函数时.

typedef void(*CallBackPtr)(int, int, void*);
class CallBack{
public:
    CallBack(CallBackPtr(fPtr), void*dataToPassBack) :func(fPtr), data(dataToPassBack){}
    void makeCallBack(int eventXLocation, int eventYLocation)const throw()//该函数调用exception specification限制程度低于自身的函数func
    {
        func(eventXLocation, eventYLocation,data);//func的调用,有可能违反exception specification,因为没有办法知道func可能抛出什么以异常
    }
private:
    CallBackPtr func;//CallBack发生时所要调用的函数
    void *data;//用来传给CallBack函数的数据
};

上述代码makeCallBack函数,仍会出现"函数调用一个exception specification限制程度低于自身的函数"的问题。

  解决方法就是将

typedef void(*CallBackPtr)(int, int, void*);

    改为:

typedef void(*CallBackPtr)(int, int, void*) throw();

正如1所提,对于以下代码编译器会阻止并报错:

void callBackFunc1(int,int,void*);
void *callBackData;
...
CallBack c1(callBackFunc1,callBackData);//callBackFunc1可能会抛出异常,不能将callBackFunc1赋值给func(调用构造函数时)

(一个拥有exception specification的函数指针只能被赋予一个有着相同或更为局限的exception specification的函数地址)

void callBackFunc2(int,int,void*) throw();
void *callBackData;
...
CallBack c2(callBackFunc2,callBackData);//可以
 3). 处理"系统可能抛出的异常".例如内存分配失败时抛出的bad_alloc异常(由operator new和operator new[]抛出).

        除了以上预防未预期exception的方法之外,还有一种方法可以处理未预期exception:自行指定当未预期的exception抛出时所调用的函数(而不是采用默认的terminate函数),通过调用set_unexpected函数加以设定:

class UnexpectedException{};//所有非预期的exception objects都将被取代为此类的objects
void convertUnexcepted()
{
    throw UnexpectedException();//若抛出非预期exception,便调用此函数
}
set_unexcepted(convertUnexcepted);//以convertUnexcepted取代默认的unexcepted函数

这样一切非预期的异常都会导致convertUnexcepted被调用,即非预期的exception被新的、类型为UnexpectedException的exception取代。

如果非预期函数的替代者重新抛出当前的exception,该exception会被标准类型bad_exception取而代之".因此,如果用以上方式实现unexpected_handler,那么只要每一个exception specification都含有bad_exception,就再也不用担心非预期的exception导致程序终止执行:

void convertUnexpected(){
    throw();//抛出的是bad_exception(重新抛出当前异常)
}
3. Exception specification对于"函数希望抛出什么样的exception"提供了说明,并且对于抛出exception specification中未指定行为的情况提供了默认行为.但它也有其缺点:编译器只对它们做局部性检验导致很容易违反,可能会更上层的exception函数处理未预期的exception等.对于exception specification的使用需要明智使用.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值