异常切片 - 这是由于生成的复制构造函数吗?
-
11-09-2019 - |
题
我刚刚修复了代码中一个非常微妙的错误,该错误是由异常切片引起的,现在我想确保我准确理解发生了什么。
这是我们的异常基类、派生类和相关函数:
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;
}
}
当然,错误是外部调用最终抛出异常,而不是派生异常。我的错误是由于调用堆栈中较高的尝试捕获派生失败而导致的。
现在,我只想确保我理解 - 我相信在“throw e”行,正在使用默认的复制构造函数创建一个新的 Exception 对象。事情真的是这样吗?
如果是这样,我是否可以锁定将被抛出的对象的复制构造函数?我真的希望这种情况不再发生,并且我们的代码没有理由复制 Exception 对象(据我所知)。
请不要对我们拥有自己的异常层次结构这一事实发表评论。这是我正在努力纠正的一些旧设计(我正在取得良好的进展。我已经摆脱了自制的字符串类和许多自制的容器。)
更新:需要明确的是,在我问这个问题之前,我已经修复了这个错误(通过将“throw e”更改为“throw”)。我只是想确认正在发生的事情。
解决方案
当你抛出一个对象,你实际上投掷物体,而不是原来的副本。想一想 - 原始对象是在堆栈上,但堆栈被退绕和无效
我相信这是标准的一部分,但是我没有副本来引用。
异常的类型被抛出在catch块是收集的基本类型,而不是被抛出的对象的类型。解决这个问题的办法是throw;
而非throw e;
这将抛出原来捕获的异常。
其他提示
快速谷歌一下 表明是的,您抛出的复制构造函数是必需的并且必须是公共的。(这是有道理的,因为您正在初始化 e
然后扔掉它。)
无论如何,只要使用 throw
不指定异常对象,重新抛出捕获的异常 catch
. 。这不应该解决问题吗?
catch(Exception& e)
{
printf("Exception seen here! %s %d\n", __FILE__, __LINE__);
throw;
}
是
throw e;
投静态型e
的一个异常,而不管任何e
实际上是。在这种情况下,异常Derived
被复制到使用拷贝构造一个Exception
。
在这种情况下,可以只
throw;
要正确地得到Derived
例外冒泡。
如果你在其他一些情况下polymorphical投掷有趣,指的总是如此有用C ++ FAQ精简版。
C ++从未停止惊奇我。我已经失去了很多钱,如果获得对什么是行为的赌注!
在异常对象首先被复制到一个临时和你应该使用throw
。引用标准15.1 / 3:
一个界外球表达初始化临时对象,称为异常对象,其类型是由从静态式抛的操作数的去除任何顶层cv修饰符和调节从“T的阵列的类型确定“或‘功能分别返回T’为‘指针T’或‘函数指针返回T’,
我觉得这产生了一个非常有用的编码标准规则:
的异常层次的基类应该有一个纯虚析构函数。
或
在一个异常层次结构基类复制构造应被保护。
要么实现了当您尝试“抛E”,因为在第一种情况下,因为你不能调用拷贝构造函数不能创建一个抽象类的实例和第二编译器会发出警告的目标。