关于重新抛出原始异常的C ++异常问题

时间:2010-03-02 02:50:17

标签: c++ exception rethrow

catch中的以下append()是否会导致rethrown异常看到append()被调用的效果?

try {
  mayThrowMyErr();
} catch (myErr &err) {
  err.append("Add to my message here");
  throw; // Does the rethrow exception reflect the call to append()?
}

同样,如果我以这种方式重写它,如果myErr导出实际异常,会发生位切片吗?

try {
  mayThrowObjectDerivedFromMyErr();
} catch (myErr &err) {
  err.append("Add to my message's base class here");
  throw err; // Do I lose the derived class exception and only get myErr?
}

4 个答案:

答案 0 :(得分:134)

在这两种情况下,由于您通过引用捕获,因此您实际上正在改变原始异常对象的状态(您可以将其视为驻留在a magical memory location which will stay valid during the subsequent unwinding - 0x98e7058中的示例,如下例所示) 。然而,

  1. 在第一种情况下,由于您重新使用throw;(与throw err;不同,在0x98e7058所述的“神奇位置”保留原始异常对象及其修改 反映对append()
  2. 的调用
  3. 在第二种情况下,由于您明确地抛出了某些内容,因此将创建err副本,然后重新抛出(在不同的“魔法位置”0x98e70b0 - 因为对于所有编译器都知道err可能是堆栈中即将被展开的对象,例如e位于0xbfbce430,而不是0x98e7058的“神奇位置” ),所以在基类实例的复制构造过程中,您将丢失特定于派生类的数据
  4. 简单程序来说明正在发生的事情:

    #include <stdio.h>
    
    struct MyErr {
      MyErr() {
        printf("  Base default constructor, this=%p\n", this);
      }
      MyErr(const MyErr& other) {
        printf("  Base copy-constructor, this=%p from that=%p\n", this, &other);
      }
      virtual ~MyErr() {
        printf("  Base destructor, this=%p\n", this);
      }
    };
    
    struct MyErrDerived : public MyErr {
      MyErrDerived() {
        printf("  Derived default constructor, this=%p\n", this);
      }
      MyErrDerived(const MyErrDerived& other) {
        printf("  Derived copy-constructor, this=%p from that=%p\n", this, &other);
      }
      virtual ~MyErrDerived() {
        printf("  Derived destructor, this=%p\n", this);
      }
    };
    
    int main() {
      try {
        try {
          MyErrDerived e;
          throw e;
        } catch (MyErr& err) {
          printf("A Inner catch, &err=%p\n", &err);
          throw;
        }
      } catch (MyErr& err) {
        printf("A Outer catch, &err=%p\n", &err);
      }
      printf("---\n");
      try {
        try {
          MyErrDerived e;
          throw e;
        } catch (MyErr& err) {
          printf("B Inner catch, &err=%p\n", &err);
          throw err;
        }
      } catch (MyErr& err) {
        printf("B Outer catch, &err=%p\n", &err);
      }
      return 0;
    }
    

    结果:

      Base default constructor, this=0xbfbce430
      Derived default constructor, this=0xbfbce430
      Base default constructor, this=0x98e7058
      Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
      Derived destructor, this=0xbfbce430
      Base destructor, this=0xbfbce430
    A Inner catch, &err=0x98e7058
    A Outer catch, &err=0x98e7058
      Derived destructor, this=0x98e7058
      Base destructor, this=0x98e7058
    ---
      Base default constructor, this=0xbfbce430
      Derived default constructor, this=0xbfbce430
      Base default constructor, this=0x98e7058
      Derived copy-constructor, this=0x98e7058 from that=0xbfbce430
      Derived destructor, this=0xbfbce430
      Base destructor, this=0xbfbce430
    B Inner catch, &err=0x98e7058
      Base copy-constructor, this=0x98e70b0 from that=0x98e7058
      Derived destructor, this=0x98e7058
      Base destructor, this=0x98e7058
    B Outer catch, &err=0x98e70b0
      Base destructor, this=0x98e70b0
    

    另见:

答案 1 :(得分:13)

这个问题相当陈旧,并且答案适合于被问及的时间。 但是,我只想添加一个关于如何在 C ++ 11 中进行正确异常处理的注释,我相信这与您尝试使用append函数实现的内容非常吻合:

使用std::nested_exceptionstd::throw_with_nested

StackOverflow herehere中描述了如何在代码中获取异常的回溯,而无需调试器或繁琐的日志记录,只需简单编写一个适当的异常处理程序,它将重新抛出嵌套的异常。

由于您可以对任何派生的异常类执行此操作,因此可以向此类回溯添加大量信息! 您还可以查看我的MWE on GitHub,其中回溯看起来像这样:

Library API: Exception caught in function 'api_function'
Backtrace:
~/Git/mwe-cpp-exception/src/detail/Library.cpp:17 : library_function failed
~/Git/mwe-cpp-exception/src/detail/Library.cpp:13 : could not open file "nonexistent.txt"

答案 2 :(得分:8)

是的,重新抛出重新抛出原始异常对象,您已通过引用修改了该异常对象。您还可以捕获基类引用,通过它进行修改,并且仍然可以通过throw;重新抛出原始派生的异常类型。

答案 3 :(得分:1)

第一个问题,是的。

但是第二个,参考Vlad回答。 您需要仔细设计您的异常对象以处理复制ctor。按照惯例,基类不识别其子类,因此您很可能会丢失派生类所携带的附加数据。