我正在阅读bjarne Stroustrup撰写的C ++编程语言,特别是有关异常安全和RAII编程习惯的章节。我熟悉RAII,但没有抛出异常。实际上,我没有经常看到throw关键字的使用。当我研究标准模板库以了解模板机制和RAII习语时,我很谨慎地看到它(矢量,红/黑树......)。
如果我要使用throw关键字,我会倾向于一直使用它。因此,这种语法意味着使用try-catch子句,这使得代码看起来很难看。
您如何看待这个?是否有更好的技术来处理异常?或者我应该绝对使用throw?
感谢您的回复。
答案 0 :(得分:2)
大多数情况下,C ++异常更适合任何替代方案。实际上,我认为它使代码更漂亮。我会举个例子,希望你能理解。
假设我必须加载一个模型,并准备好渲染(我知道你可能不熟悉图形编程,但你应该明白这一点)。这意味着我有一个很大的功能,或者至少是一个大功能,可以调用其他几个较小的功能。整个过程包括打开模型文件,读取所有坐标,打开材质文件,为材质分配内存,打开纹理文件,为纹理分配内存,加载纹理等等。很多东西。
但有时候,在最后,我可能会遇到一个错误,我无法找到其中一个纹理文件,或者无法打开它。怎么处理?有小文件打开函数返回一个值来检查它的纹理加载函数,实现发生了什么不好的事情,然后返回到读取主模型文件的函数等?你认为所有返回,检查和释放每个点的资源看起来都很丑陋吗?
这是一种更好的方法:将 load_model()函数包装在try
块中,只需在需要它们的地方插入一些throw
语句。拥有所有"免费记忆" catch
部分中的代码。它会看起来更清洁,也不太可能出错。
我希望你理解这个想法。如果您有任何疑问,请询问他们。
答案 1 :(得分:2)
例外情况非常适合处理exceptional cases:
但是当错误不是异常时异常should not be used,即预期会定期发生(例如循环直到条件发生)。在这种情况下,特殊的返回值是更好的选择。
原因有三个:
terminate()
的异常。这种情况非常不可能发生。但是,如果您滥用异常处理并在几乎正常的情况下使用它作为返回的替代,则增加在这种情况下成为一天的可能性,除非you'd take extreme care。 答案 2 :(得分:1)
当我有一个必须返回的功能时,我只使用throw,我可以。当我返回指针时,空指针就足够了,但是当返回引用时,你什么都不能返回,所以我扔了。其中一部分,我从不扔,而且我从来没有抓过,因为你说的原因。
对于带有错误处理的C ++,还有更好的方法。首先,有后续C ++版本的函数契约。它允许你这样做:
struct Database {
SomeResult query(std::string) [[expect: connected]] {
// ...
}
private:
bool connected = false;
};
错误处理程序是可自定义的,因此可能是异常,可能是std::terminate
。
也有std::expected
提案。这是一个实用程序,您现在可以使用boost实现。
它允许函数返回结果,或返回错误。然后,函数的用户可以以比catch
更漂亮的方式正确处理它。考虑这个例子,取自提案:
根据预期,我们不需要使用例外,我们可以使用 std :: error_condition比内省更容易内省 std :: exception_ptr如果我们想要使用错误。为了...的目的 在这个例子中,我们使用以下枚举(样板代码 关于std :: error_condition未显示):
enum class arithmetic_errc { divide_by_zero, // 9/0 == ? not_integer_division // 5/2 == 2.5 (which is not an integer) };
使用预期,代码变为:
expected<double,error_condition> safe_divide(double i, double j) { if (j==0) return make_unexpected(arithmetic_errc::divide_by_zero); // (1) else return i / j; // (2) }
在提案中,它表明您可以更改用户功能:
例如,基于异常的函数i + j / k是:
double f1(double i, double j, double k) { return i + safe_divide(j,k); }
但变得使用预期:
expected<double, error_condition> f1(double i, double j, double k) { auto q = safe_divide(j, k) if(q) return i + *q; else return q; }
稍后会显示您可以使用std::expected::map
编写此缩短版本:
expected<double, error_condition> f1(double i, double j, double k) { return safe_divide(j, k).map([&](double q){ return i + q; }); }
您可以在此处阅读这两项提案:Simple Contact for C++和std::expected proposal