129

catch中后面的append()会不会导致rethrow的异常看到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

4 回答 4

166

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

  1. 在第一种情况下,由于您重新抛出throw;throw err;0x98e7058
  2. 在第二种情况下,由于您明确地抛出一些东西,因此将创建一个副本然后重新抛出(在不同的“神奇位置” -因为编译器知道的所有可能是堆栈上即将展开的对象,就像是at ,而不是在“神奇位置” at ),因此在基类实例的复制构造过程中,您将丢失派生类特定的数据。err0x98e70b0erre0xbfbce4300x98e7058

简单的程序来说明正在发生的事情:

#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

另见:

于 2010-03-02T03:12:29.510 回答
30

这个问题相当古老,并且有一个适合被问到的时间的答案。但是,我只想添加一条说明,说明自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"
于 2017-12-03T13:21:58.347 回答
9

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

于 2010-03-02T03:07:20.383 回答
1

对于第一个问题,是的。

但对于第二个,请参阅弗拉德的回答。您将需要仔细设计您的异常对象来处理复制 ctor。按照惯例,基类无法识别其子类,因此您很可能会丢失派生类携带的额外数据。

于 2010-03-02T03:09:06.533 回答