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

Wil*_*mKF 109 c++ exception rethrow

catch中的以下append()是否会导致重新抛出的异常以查看append()被调用的效果?

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

类似地,如果我以这种方式重写它,如果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?
}
Run Code Online (Sandbox Code Playgroud)

vla*_*adr 145

在这两种情况下,由于您通过引用捕获,因此您实际上正在改变原始异常对象的状态(您可以将其视为驻留在一个神奇的内存位置,该位置将在后续展开期间保持有效 - 0x98e7058在下面的示例中).然而,

  1. 在第一种情况下,由于你重新使用throw;(与throw err;保存原始异常对象不同,修改后,在所说的"魔法位置" 0x98e7058)反映对append()的调用
  2. 在第二种情况下,因为你扔东西明确,一个副本err将被创建,然后重新抛出(在不同的"神奇的位置" 0x98e70b0-因为所有的编译器知道err可能是堆栈有关的物体被unwinded,就像e是at 0xbfbce430,而不是在"魔术位置" 0x98e7058),因此在基类实例的复制构造期间,您将丢失特定派生类的数据.

简单的程序来说明发生了什么:

#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;
}
Run Code Online (Sandbox Code Playgroud)

结果:

  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
Run Code Online (Sandbox Code Playgroud)

另见:


GPM*_*ler 20

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

使用std::nested_exceptionstd::throw_with_nested

它在StackOverflow 这里这里描述了如何通过简单地编写一个适当的异常处理程序来重新抛出嵌套异常,如何在代码内部获得异常回溯,而无需调试器或繁琐的日志记录.

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

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"
Run Code Online (Sandbox Code Playgroud)


Tro*_*nic 8

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