我希望能够抛出一个构造对象,但是在抛出之前修改它 (使用Named Parameter Idiom)。 给出:
#include <iostream>
#include <exception>
using namespace std;
struct my_exception : std::exception {
my_exception() {
cout << "my_exception(): this=" << hex << (unsigned long)this << endl;
}
my_exception( my_exception const& ) {
cout << "my_exception( my_exception const& )" << endl;
}
~my_exception() throw() {
cout << "~my_exception()" << endl;
}
my_exception& tweak() {
return *this;
}
char const* what() const throw() { return "my_exception"; }
};
int main() {
try {
throw my_exception().tweak();
}
catch ( my_exception const &e ) {
cout << "&e=" << hex << (unsigned long)&e << endl;
}
}
当我运行程序时,我得到:
my_exception(): this=7fff5fbfeae0
my_exception( my_exception const& )
~my_exception()
&e=1001000f0
~my_exception()
如您所见,捕获的异常对象不是最初抛出的异常对象。
如果我删除了对tweak()
的调用,我会改为:
my_exception(): this=1001000f0
&e=1001000f0
~my_exception()
对于调用tweak()
的情况,为什么要调用复制构造函数?我希望tweak()
对最初构造的对象进行操作,而不进行复制。有没有办法阻止复制?
仅供参考:我正在使用g ++ 4.2.1(Mac OS X上的Xcode的一部分)。
答案 0 :(得分:5)
按值抛出异常。您不能将引用作为引用。尝试时,会复制对象(使用静态已知类型)。
顺便说一句,这就是为什么让异常可以克隆,以及使用虚拟重新驱动方法的好主意。
编辑(参见评论):例如,通过C回调传播异常是未定义的行为。但是如果你已经定义了一个合适的异常类,那么你可以克隆它,并且在C ++中 - 再次通过虚拟方法重新调用调用链重新抛出。
干杯&amp;第h。,
答案 1 :(得分:3)
要添加到Alf的答案,当您不调用tweak()
时未获得复制操作这一事实是因为标准允许(但不要求)将复制构造函数的调用省略到创建临时异常对象。来自C ++ 03 15.1 / 5(抛出异常):
如果使用临时对象即可 被取消而不改变 程序的含义除了 执行构造函数和 与使用相关的析构函数 临时对象(12.2),然后是 处理程序中的异常可以 直接用参数初始化 抛出表达式。当。。。的时候 抛出的对象是一个类对象,和 用于复制构造函数 初始化临时副本不是 方案是不正常的 (即使临时对象可以 否则被淘汰)。
如果你将拷贝构造函数设为私有,gcc会给你一个错误(即使构造函数是公共的,它也不会被调用)。 MSVC不会给出错误,但我认为应该这样做。
答案 2 :(得分:0)
AFAIK您的第throw my_exception().tweak();
行发生了以下情况:
创建新的my_exception对象(在本地,在堆栈上),tweak()返回对此本地对象的引用。然后,当您抛出此引用时,您将超出范围并删除本地对象。因此,实现将类复制到动态内存以保持引用有效。
在第二种情况下,您按值抛出它,它会立即分配到动态内存中。