异常切片 - 这是由于生成的复制构造函数?

Mic*_*hne 10 c++ exception object-slicing

我刚刚在代码中修复了一个非常微妙的错误,这是由异常切片造成的,我现在想确保我完全理解发生了什么.

这是我们的基本异常类,派生类和相关函数:

class Exception
{
public:
  // construction
  Exception(int code, const char* format="", ...);
  virtual ~Exception(void);

  <snip - get/set routines and print function>

protected:
private:
  int mCode;                // thrower sets this
  char mMessage[Exception::MessageLen]; // thrower says this FIXME: use String
};

class Derived : public Exception {
public:
  Derived (const char* throwerSays) : Exception(1, throwerSays) {};
};

void innercall {
  <do stuff>
  throw Derived("Bad things happened!");
}

void outercall {
  try {
    innercall();
  }
  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw e;
  }
}
Run Code Online (Sandbox Code Playgroud)

该bug当然是外部调用最终抛出异常,而不是Derived.我的错误是由于调用堆栈中的高位尝试捕获Derived失败而导致的.

现在,我只想确保理解 - 我相信在'throw e'行,使用默认的复制构造函数创建一个新的Exception对象.这是真的发生了什么?

如果是这样,我是否可以锁定将被抛出的对象的复制构造函数?我真的更喜欢这种情况不会再次发生,我们的代码没有理由复制Exception对象(我​​知道).

请不要评论我们有自己的异常层次结构这一事实.这是一个我正在努力纠正的旧设计(我正在取得良好的进展.我已经摆脱了本土的字符串类,以及许多本土容器.)

更新:要明确的是,在我提出问题之前,我已经修复了错误(通过将'throw e'更改为'throw').我只是想确认发生了什么.

Mar*_*som 21

抛出一个对象时,实际上是在抛出一个对象的副本,而不是原始对象的副本.想一想 - 原始对象在堆栈中,但堆栈正在展开并失效.

我相信这是标准的一部分,但我没有副本可供参考.

catch块中抛出的异常类型是catch的基本类型,而不是抛出的对象的类型.绕过这个问题的方法是,throw;而不是throw e;将抛出原始捕获的异常.


jal*_*alf 10

一个快速的谷歌建议是的,你要扔复制构造函数是必需的,必须是公开的.(这是有道理的,因为你正在初始化一个副本e并抛出它.)

无论如何,只需使用throw而不指定异常对象,重新抛出在中捕获的内容catch.不应该解决问题吗?

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }
Run Code Online (Sandbox Code Playgroud)


laa*_*lto 7

是.

throw e;
Run Code Online (Sandbox Code Playgroud)

抛出静态类型的异常,e无论e实际是什么.在这种情况下,Derived异常将复制到Exception使用复制构造函数.

在这种情况下你可以

throw;
Run Code Online (Sandbox Code Playgroud)

Derived正确地获取异常气泡.

如果你对其他一些情况下的多态投掷很感兴趣,请参阅总是如此有用的C++ FAQ Lite.