假设我在每个级别上嵌套了方法a,b,c,d,e,我们在正常的操作过程中返回错误,但是e也可能抛出异常(例如STL插入时的内存不足)。例外非常少,实际展开的速度和速度有多快不是问题。
在这种情况下,最合适的异常处理策略是什么?
优点:不需要编写异常安全代码,最简单的实现,最容易测试,最容易理解,以及展开所需的最小编译时间信息。
缺点:看起来并不酷,添加明显的try / catch clatter - 实际上围绕每个insert和push_back,直到在STL容器周围编写异常安全包装器的程度,有观点认为try块的运行时性能损失(并且有意见认为没有任何惩罚)。
优点:看起来很酷,没有咔嗒声。
缺点:很难直观地验证中间的所有代码确实是异常安全的,以测试所有异常展开路径
优点:可预测,可以容忍异常安全代码中的轻微瑕疵,比崩溃更好。
缺点:太苛刻了
void a()
{
...
x = b();
...
}
int b()
{
y = c();
...
return y + d();
}
int d()
{
...
z = e();
...
}
答案 0 :(得分:6)
一般来说,我觉得系统最好是通过使用单一的错误处理策略来实现 - 每次在两者之间进行转换时,都会出现问题。大多数时候,我更喜欢任何合理支持它们的语言中的异常(即,不在C语言中 - setjmp / longjmp被诅咒)。鉴于您所描述的设计,大多数系统已经使用了错误代码,将异常转换为e
中的错误代码并假装系统中不存在异常似乎是有意义的。
另一方面,您确定只有e
可以抛出异常吗?在C ++中,从一个你通常不会想到的地方抛出异常相当容易 - 调用new
,几乎任何标准库的使用,等等。
请注意,当您使用
等代码时,使用RIAA等技术的异常安全性也很有用int* workspace[] = new int[500];
...
if(some_function() == ERROR)
return SOME_FUNCTION_FAILED; // oops just leaked memory
...
delete[] workspace;
(或其他获取资源的东西)
RIAA技术被认为与异常有关,但实际上只是因为它们完全是必要的(因为你无法做到这一点),使用错误代码处理技术它们只是非常有用;虽然理论上你可以自己处理所有的资源释放,但实际上你有时也会忘记。让编译器去做(tm)
你真的需要尝试/捕捉每一个操作吗?为什么不将整个函数包装在try / catch中并返回错误。首先,它可以更容易区分,例如,读取错误std::vector
索引的失败和抛出std::bad_alloc
的内存分配失败。
try / catch的性能损失在很大程度上取决于ABI和编译器。我相信具有现代GCC的现代ABI(例如x86-64 Unix ABI)成本非零但很小,但在其他编译器上它可以引人注目。如果你真的想知道,你必须在你的特定编译器/平台上运行实验。
答案 1 :(得分:1)
无论你在这里做出什么决定,我都会鼓励你将异常安全的概念砸到其他开发者的头脑中 - 或者至少轻轻地点击它。根据我的经验,编写异常安全代码的过程产生了更加干净的交易代码。
作为一种好处,无论是否存在异常,编码风格都有效,而反之亦然。
答案 2 :(得分:0)
我会根据几个因素做出决定。
1)您的代码库有多干净。如果您的代码库相对干净,则更容易验证您的代码是异常安全的,并且您可以在顶部捕获异常。但是如果代码是混乱的,那么将更容易捕获异常并依赖现有的错误处理。
2)你的团队有多好。假设您不是唯一编码的人,另一个要处理的问题是您的所有团队成员是否都在编写异常安全代码。如果你的团队中有人不能打破旧习惯,那么你应该抓住你的例外,因为程序员可能会随着时间的推移引入异常不安全的代码。