10

我刚刚在我们的代码中修复了一个由异常切片引起的非常微妙的错误,现在我想确保我完全理解正在发生的事情。

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

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;
  }
}

错误当然是外部调用最终抛出异常,而不是派生。我的错误是由于调用堆栈中较高的尝试捕获 Derived 失败造成的。

现在,我只是想确保我理解 - 我相信在“throw e”行,正在使用默认的复制构造函数创建一个新的异常对象。真的是这样吗?

如果是这样,我是否可以为将被抛出的对象锁定复制构造函数?我真的希望这种情况不再发生,并且我们的代码没有理由复制 Exception 对象(我知道)。

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

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

4

4 回答 4

22

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

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

在 catch 块中抛出的异常类型是 catch 的基本类型,而不是抛出的对象的类型。解决这个问题的方法是抛出原始捕获的异常,throw;而不是throw e;抛出原始捕获的异常。

于 2009-07-07T22:43:18.613 回答
10

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

无论如何,只需使用throw而不指定异常对象,即可重新抛出catch. 这不应该解决问题吗?

  catch(Exception& e)
  {
    printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
    throw;
  }
于 2009-07-07T22:45:14.943 回答
7

是的。

throw e;

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

在这种情况下,您可以

throw;

使Derived异常冒泡正确。

如果您对其他情况下的多态抛出感兴趣,请参阅总是如此有用的 C++ FAQ Lite

于 2009-07-07T22:51:31.467 回答
1

C++ 从未停止让我惊讶。如果这是对行为的赌注,我会损失很多钱!

异常对象首先被复制到一个临时对象,您应该使用throw. 引用标准 15.1/3:

throw-expression 初始化一个临时对象,称为异常对象,其类型通过从 throw 操作数的静态类型中删除任何顶级 cv 限定符并从“T 数组”或“调整类型”来确定返回 T 的函数”分别指向“指向 T 的指针”或“指向返回 T 的函数的指针”。

我认为这产生了一个非常有用的编码标准规则:

异常层次结构的基类应该有一个纯虚析构函数。

或者

异常层次结构中基类的复制构造函数应受到保护。

要么实现编译器在您尝试“抛出 e”时发出警告的目标,因为在第一种情况下您无法创建抽象类的实例,而在第二种情况下,您无法调用复制构造函数。

于 2009-07-08T10:53:12.643 回答