【问题标题】:An exception gets thrown twice from a constructor with a function-try-block带有函数尝试块的构造函数两次抛出异常
【发布时间】:2013-11-10 07:34:12
【问题描述】:

为什么A类的构造函数抛出的以下异常会被两次捕获,第一次是构造函数本身的catch,第二次是main函数中的catch?

为什么构造函数中的 catch 不只捕获一次?

 #include <iostream>
    using namespace std;

    class E {
       public:
          const char* error;
          E(const char* arg) : error(arg) { }
    };

    class A {
       public:
          int i;

          A() try : i(0) {
             throw E("Exception thrown in A()");
          }
          catch (E& e) {
             cout << e.error << endl;
          }
    };

    int main() {

       try {
          A x;
       }
       catch(...) 
       {
        cout << "Exception caught" << endl; 
       }
    }

如果我删除 main 函数中的 try-catch 块,程序将崩溃。 这是输出:

Exception thrown in A()
terminate called after throwing an instance of 'E'
zsh: abort (core dumped)  ./main

为什么在 main 函数中没有 try-catch 块会崩溃?

【问题讨论】:

  • 另外,如果你把try inside 构造函数的主体,你会捕获一个异常。
  • @MM。是的,这是为什么呢?如果没有 main 中的 try-catch 块,它就不会再崩溃了。
  • 请不要抓住省略号...正确并抓住适当的异常。
  • 您最好使用const 引用来捕获异常。
  • 因为你通常不想在投掷/接球后改变它们,如果你需要创建一个新副本并重新投掷这个。

标签: c++ exception exception-handling constructor try-catch


【解决方案1】:

构造函数中的函数尝试块不能防止异常。一旦构造函数中发生异常,你没有对象,异常必须传播。 function-try-block 唯一能做的就是进行一些本地清理。

就函数尝试块而言,构造函数确实是一种非常特殊的动物。

参照。 C++11 15.3/14:

如果控制到达构造函数或析构函数的 function-try-block 的处理程序的末尾,则重新抛出当前处理的异常。


Tl;dr:永远不要使用函数尝试块。

【讨论】:

  • 这是否意味着它捕获但随后失败,尽管已经执行了捕获块?
  • 但是为什么它会在构造函数中被 try-catch 捕获一次呢?函数中的function-try-blocks和try-catch blocks有什么区别?
  • @takwing:是的。初始化期间抛出的异常意味着该对象无法正确构造,因此不应该存在。如果您在外部捕获异常,则可以处理对象构造失败的事实。如果您在构造函数的主体内部捕获异常,则它不会是源自初始化的异常,而是源自在成功构造之后执行的某些语句已经发生的异常.
  • @takwing:范围不同。一个普通的 try/catch 块是 inside 函数体范围。函数尝试块环绕函数体范围。
  • @KerrekSB:顺便提一下,如果有的话,function-try-block 的实际用例是什么?
【解决方案2】:

这似乎是合乎逻辑的。考虑以下两种情况。

我。 Try 块在构造函数的主体内:

  A() : i(0) {
    try
    {
       throw E("Exception thrown in A()");
    }
    catch (E& e) {
       cout << e.error << endl;
    }
    // If code reaches here,
    // it means the construction finished well
  }

二。 Try 块在初始化器 ctor 中:

  A() try : i(0) {
     throw E("Exception thrown in A()");
  }
  catch (E& e) {
     cout << e.error << endl;

     // OK, you handled the exception,
     // but wait you didn't construct the object!
  }

在第一种情况下,发生异常后,您将在构造函数中对其进行处理,然后您将正确地构造对象。

在第二种情况下,发生异常后,您将在那里处理它。 但是您还没有构造对象,并且调用方没有对象。调用者应处理未构造对象的情况。

【讨论】:

  • @M M,调用者捕获的第二个异常与构造函数抛出的第一个异常不同吗?按照你的解释,第二个应该是对象构造不完整导致的异常吧?
  • @takwing:不,它们是一样的。是的。事实上,第二种情况会重新抛出异常。
【解决方案3】:

您正在使用名为function-try-catch 的功能。在构造函数中使用时,它允许在初始化列表中捕获异常(对于在基类构造函数中捕获异常特别有用)以及构造函数主体。但是由于异常是在构造函数中抛出的,所以类是不完整的,所以编译器会自动重新抛出任何捕获的异常。 这就是为什么你看到它被抓了两次。

阅读以下文章了解更多详情:

Constructors and Exception in C++

【讨论】:

    猜你喜欢
    • 2011-11-04
    • 1970-01-01
    • 2017-08-28
    • 2014-11-03
    • 1970-01-01
    • 2015-03-11
    • 1970-01-01
    • 1970-01-01
    • 2019-07-11
    相关资源
    最近更新 更多