c++抛出异常(需要建议)

C++ throwing exceptions (need advices)

本文关键字:抛出异常 c++      更新时间:2023-10-16

我正在阅读bjarne Stroustrup的《c++编程语言》,特别是关于异常安全和RAII编程习惯的章节。我熟悉RAII,但不熟悉抛出异常。实际上,我并没有看到经常使用throw关键字。当我为了理解模板机制和RAII习语而学习标准模板库时,我很少看到它(向量,红/黑树,…)。

如果我要使用throw关键字,我将倾向于一直使用它。因此,这种语法意味着使用try-catch子句,这会使代码看起来很难看。

你觉得这个怎么样?是否有更好的技术来处理异常?或者我绝对应该使用throw?

谢谢你的回复。

首先,这些链接可能会有所帮助。链接1链接2

大多数情况下,c++异常比其他任何方法都更适合。实际上,我认为它使代码更漂亮。我给你举个例子,希望你能理解。

假设我必须加载一个模型,并准备在屏幕上渲染它(我知道您可能不熟悉图形编程,但您应该明白这一点)。这意味着我有一个大函数或者至少是一个大函数它调用了其他几个小函数。整个过程包括打开模型文件、读取所有坐标、打开材质文件、为材质分配内存、打开纹理文件、为纹理分配内存、加载纹理等。很多东西。

但有时,在最后,我可能会遇到一个错误,我找不到一个纹理文件,或者无法打开它。如何处理?是否有小文件打开函数返回一个值给纹理加载函数谁检查它,实现一些不好的事情发生,并反过来返回一些东西给函数读取主模型文件等?你不觉得所有在每个点返回、检查和释放资源看起来很丑陋吗?

这里有一个更好的方法:将load_model()函数包装在try块中,然后在需要的地方插入一些throw语句。并有所有的"自由内存"代码在catch部分。这样看起来干净多了,也不太可能出错。

我希望你能理解这个想法。如果你有任何问题,请问他们。

我只在函数必须返回某些东西并且我可以返回时才使用throw。当我返回指针时,一个空指针就足够了,但是当返回引用时,没有什么可以返回的,因此我抛出。有一部分,我从来不扔,也从来不接,因为你告诉我的原因。

c++有更好的错误处理功能。首先,在以后的c++版本中会有函数契约。它允许你这样做:
struct Database {
    SomeResult query(std::string) [[expect: connected]] {
        // ...
    }
private:
    bool connected = false;
};

错误处理程序是可定制的,所以它可能是例外,它可能是std::terminate


也有std::expected提案。这是一个实用程序,你现在可以使用与boost实现。

允许函数要么返回结果,要么返回错误。然后,函数的用户可以以比catch更美观的方式正确地处理它。考虑下面这个取自提案的例子:

对于expected,我们不需要使用异常,我们可以使用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,代码变成:

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);
}

变成了using expected:

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;
    });
}

你可以在这里阅读这两个提案:c++的Simple Contact和std::expected proposal

异常对于处理异常情况非常有用:

  • 阻止构造函数完成其工作的错误。
  • 预期不会发生的错误。一个典型的例子是内存分配问题
  • 可能会级联的错误,因为它们表明明显的不利情况

但是,当错误不是异常时,即期望它有规律地发生(例如循环直到一个条件发生),不应该使用异常。在这种情况下,一个特殊的返回值是更好的选择。

原因有三:

  • 概念性的:"exception"这个词强烈地暗示了一种特殊的情况。
  • 性能:在现代编译器中,进入try块几乎没有明显的性能影响:这是一种非常便宜的保护。但是,当你抛出时,很明显,堆栈展开的代价比返回一些值要高得多("代价要高得多",但仍然比显示消息要快……对于一个数量级:在我的旧i7 cpu上,一个抛出大约7µs,相比之下,等效返回)
  • 的纳秒级别
  • 健壮性:如果作为堆栈展开的一部分而被销毁的对象抛出自己的异常,异常处理会突然terminate()您的程序。这种情况不太可能发生。但是,如果您滥用异常处理并在几乎正常的情况下使用它作为返回的替代品,那么除非您非常小心,否则就会增加有一天遇到这种情况的可能性。