我刚刚花了几个小时阅读关于何时使用异常的 SO 问题,似乎有两个观点不同的阵营:
- 对错误代码使用异常
- 大部分时间使用错误代码,仅在发生某些灾难性错误时才异常(exception)
这只是一个没有被广泛接受的最佳实践的有争议的话题吗?
最佳答案
正如您可能从大量答案中得出的结论,肯定没有共识。
在语义上,异常和错误提供完全相同的功能。实际上,它们在所有语义方面都是相同的,并且可以像异常一样任意丰富错误(您不必使用简单的代码,您可以使用真正的数据包!)。
唯一的区别是它们的传播方式:
- 必须手动传递错误
- 异常会自动传播
另一方面:
- 签名中完美记录了错误的可能性
- 异常在代码检查中沉默(阅读 GotW #20: Code Complexity 并哭泣),而隐藏执行路径使推理更加困难。
这两种解决方案都显得笨拙的原因仅仅是错误检查很困难。事实上,我每天编写的大部分代码都与错误检查有关,无论是技术上的还是功能上的。
那该怎么办?
警告:前面的演示,如果您只关心答案,请跳到下一部分
我个人喜欢在这里利用类型系统。典型的例子是指针引用二分法:指针就像一个引用,可以为空(并且可以重新定位,但在这里无关紧要)
因此,而不是:
// Exceptions specifications are better not used in C++
// Those here are just to indicate the presence of exceptions
Object const& Container::search(Key const& key) const throw(NotFound);
我会倾向于写:
Object const* Container::search(Key const& key) const;
或者更好的是,使用聪明的指针:
Pointer<Object const> Container::search(Key const& key) const;
template <typename O>
O* Pointer<O>::operator->() const throw(Null);
template <typename O>
O& Pointer<O>::operator*() const throw(Null);
在这里我发现使用异常是多余的,原因有两个:
- 如果我们正在搜索一个对象,那么找不到它既是一种非常常见的情况,也没有太多数据可以携带:错误原因? 它不存在
- 客户不一定认为它不存在是一个错误,我有什么资格认为我比她更了解她的业务?我是谁来决定永远不会出现不找到所要求的东西不合适的情况?
我对异常本身没有问题,但它们会使代码变得笨拙,请考虑:
void noExceptions(Container const& c)
{
Pointer<Object const> o = c.search("my-item");
if (!o) {
o = c.search("my-other-item");
}
if (!o) { return; } // nothing to be done
// do something with o
}
并将其与“异常”情况进行比较:
void exceptions(Container const& c)
{
Object const* p = 0;
try {
p = &c.search("my-item");
}
catch(NotFound const&) {
try {
p = &c.search("my-other-item");
}
catch(NotFound const&) {
return; // nothing to be done
}
}
// do something with p
}
在这种情况下,使用异常似乎并不合适:/
另一方面:
try {
print() << "My cute little baby " << baby.name() << " weighs " << baby.weight();
}
catch(Oupsie const&) {
// deal
}
当然比:
if (!print("My cute little baby ")) { /*deal*/ }
if (!print(baby.name())) { /*deal*/ }
if (!print(" weighs ")) { /*deal*/ }
if (!print(baby.weight())) { /*deal*/ }
那么什么是最好的呢?
这取决于。像所有工程问题一样,没有 Elixir ,一切都是让步。
所以请记住两件事:
- 错误报告是 API 的一部分
- API 的设计应考虑到易用性
如果您发现自己想知道是否使用异常,只需尝试使用您的 API。如果没有明确的赢家,那就是:没有理想的解决方案。
哦,当发现在设计时选择的错误报告机制不再合适时,请毫不犹豫地重构您的 API。不要害臊:需求会随着时间而变化,所以 API 也会随之变化。
个人我倾向于只对不可恢复的错误使用异常:因此我的代码中很少尝试/捕获,仅在最外层,以准确记录错误(爱堆栈帧)并记录以及 BOM 的转储。
这与 Haskell 非常相似(并且确实受到了强烈的影响),那里的代码被隔离在两个明确的部分中:虽然任何都可以抛出异常,但只有 IO 部分(外部部分)可以真正捕获它们。因此,纯部分必须以其他方式处理错误情况,以防它们“正常”。
但是,如果我遇到一个问题,即使用异常使代码更易于阅读且更自然(这是主观的),那么我使用异常:)
关于c++ - C++ 社区是否就何时应使用异常达成普遍共识?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5609503/