2011-03-22 17 views
1

我希望能够抛出一个构造对象,但在抛出之前对其进行修改 (使用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的一部分)。

回答

5

按值抛出异常。您不能将引用作为参考。当你尝试时,对象被复制(使用静态已知类型)。

顺便说一句,这是为什么做出异常可复制是一个好主意,并有虚拟rethrower方法。

编辑(请参阅注释):例如,它是未定义的行为,通过C回调传播异常。但是,如果你已经定义了一个合适的异常类,那么你可以克隆它,并在C++中 - 再次通过虚拟方法重新调用调用链。 。

干杯&心连心,

+0

那么,如何修改我的示例以使用clone()和/或虚拟rethrower方法? – 2011-03-22 06:26:33

+0

@保罗:我不会。但是,当我写这篇文章时,你并没有解释你的“调整”方法是什么意思。对此猜测,它意味着要做一些额外的初始化,一个好的方法是改为定义派生类并在'throw'语句中使用该类。干杯&hth。, – 2011-03-22 06:29:38

+2

不明白需要虚拟重新投掷者。如果你通过引用来捕获并使用'throw;'重新抛出,那么一旦实现抛出异常,将不会有更多的副本。 – 2011-03-22 07:27:31

0

AFAIK以下发生在你行throw my_exception().tweak();:创建

新my_exception对象(本地,在栈上),调整()返回的引用,这个本地对象。然后,当你抛出这个引用时,你离开了范围,本地对象被删除。所以,实现将类复制到动态内存以保持引用的有效性。

在第二种情况下,您按值抛出它并立即将其分配到动态内存中。

3

要添加到Alf的答案,当您不拨打tweak()时没有获得复制操作的事实是因为标准允许(但不要求)消除对拷贝构造函数的调用以创建临时异常对象。从C++ 03 15.1/5(抛出异常):

如果使用临时对象的可 而不改变节目的 含义除了 执行构造和相关的析构函数 可以消除使用 临时对象(12.2),则处理程序中的异常可以是 ,并直接使用throw表达式的参数 进行初始化。当 抛出的对象是一个类的对象,并 用于 拷贝构造函数初始化临时副本不是 访问,程序形成不良的 (即使暂时对象可以 否则被淘汰)。

如果你使拷贝构造函数为私有的,gcc会给你一个错误(即使构造函数是public的时候它不会被调用)。 MSVC不会给出错误,但它应该我想。

相关问题