面试题答案
一键面试异常规格说明的作用
- 告知调用者:异常规格说明能让函数调用者提前知道该函数可能抛出哪些类型的异常。例如
void func() throw(int, char)
表明函数func
可能抛出int
或char
类型的异常。这样调用者在调用该函数时可以针对性地编写catch
块来捕获可能出现的异常,增强了代码的健壮性和可维护性。 - 强制函数实现者:函数实现者必须遵守异常规格说明,如果函数内部抛出了不在规格说明中的异常类型,程序会调用
std::unexpected
函数,默认情况下std::unexpected
会调用std::terminate
终止程序。这有助于确保函数的异常行为是可预测的。
被弃用的原因
- 维护困难:当函数的实现细节发生变化,可能需要抛出新的异常类型时,不仅要修改函数内部代码,还需要同时更新异常规格说明。如果忘记更新异常规格说明,程序可能在运行时调用
std::unexpected
并终止,这增加了维护成本和出错的可能性。 - 缺乏灵活性:异常规格说明是一种静态的声明方式,无法适应运行时的动态情况。例如,根据不同的输入或运行环境,函数可能需要抛出不同类型的异常,而异常规格说明很难满足这种动态需求。
- 实际应用问题:在实际项目中,异常规格说明并没有得到广泛有效的使用。许多开发者发现它带来的好处不足以弥补其带来的复杂性和维护成本,导致很多代码并没有严格按照异常规格说明来编写和调用函数,使得这一特性的实用性大打折扣。
替代方案
- 文档说明:通过代码注释或独立的文档来描述函数可能抛出的异常类型。例如在函数定义上方添加注释
// func 可能抛出 std::runtime_error 异常
。这种方式简单灵活,不需要修改代码的语法结构,维护起来相对容易。 - 异常类型体系:使用继承和多态构建合理的异常类型体系。例如,定义一个基类异常
std::exception
,然后从它派生出自定义的异常类型,如class MyException : public std::runtime_error
。函数可以抛出这些派生类异常,调用者可以通过捕获基类异常来处理多种相关的异常情况,同时也可以通过捕获派生类异常来进行更具体的处理。这样在函数声明中不需要显式列出所有可能抛出的异常类型,增强了代码的灵活性和可维护性。