C++ 抛出对象复制构造——为什么?

C++ 抛出对象复制构造——为什么?,c++,exception-handling,C++,Exception Handling,我希望能够抛出一个构造的对象,但在抛出之前对其进行修改 (使用)。 鉴于: 如您所见,捕获的异常对象不是最初抛出的对象。 如果我删除对tweak()的调用,我会得到: my_exception(): this=1001000f0 &e=1001000f0 ~my_exception() 对于调用tweak()的情况,为什么要调用复制构造函数?我希望tweak()对最初构造的对象进行操作,而不复制。有没有办法防止复制构造 仅供参考:我使用的是g++4.2.1(MacOSX上Xcode的一

我希望能够抛出一个构造的对象,但在抛出之前对其进行修改 (使用)。 鉴于:

如您所见,捕获的异常对象不是最初抛出的对象。 如果我删除对
tweak()
的调用,我会得到:

my_exception(): this=1001000f0
&e=1001000f0
~my_exception()
对于调用
tweak()
的情况,为什么要调用复制构造函数?我希望
tweak()
对最初构造的对象进行操作,而不复制。有没有办法防止复制构造


仅供参考:我使用的是g++4.2.1(MacOSX上Xcode的一部分)。

值引发异常。您不能将引用作为引用抛出。尝试时,会复制对象(使用静态已知类型)

顺便说一句,这就是为什么让异常可克隆是一个好主意的原因之一,并且使用虚拟的恢复方法

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


干杯,

值引发异常。您不能将引用作为引用抛出。尝试时,会复制对象(使用静态已知类型)

顺便说一句,这就是为什么让异常可克隆是一个好主意的原因之一,并且使用虚拟的恢复方法

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


干杯&hth.,

AFAIK在您的行中发生以下情况
抛出我的异常().tweak()

创建新的my_异常对象(本地,在堆栈上),tweak()返回对此本地对象的引用。然后,当抛出此引用时,您将超出范围,本地对象将被删除。因此,实现将类复制到动态内存以保持引用有效


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

AFAIK在您的行中发生以下情况
抛出我的异常()

创建新的my_异常对象(本地,在堆栈上),tweak()返回对此本地对象的引用。然后,当抛出此引用时,您将超出范围,本地对象将被删除。因此,实现将类复制到动态内存以保持引用有效


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

为了补充Alf的答案,当您不调用
tweak()
时,您不会得到复制操作,这是因为标准允许(但不要求)省略对复制构造函数的调用以创建临时异常对象。来自C++03 15.1/5(引发异常):

如果可以使用临时对象 在不改变环境的情况下被淘汰 除以下内容外,本计划的含义: 施工人员和承包商的执行 与使用 临时对象(12.2),然后 处理程序中的异常可以是 直接用参数初始化 扔的表情。当 抛出的对象是类对象,并且 复制构造函数用于 未初始化临时副本 如果可以访问,则程序的格式不正确 (即使临时对象可能 否则将被淘汰)


如果您将复制构造函数设置为私有,gcc将给您一个错误(即使当构造函数是公共的时,它也不会被调用)。MSVC不会给出错误,但我认为应该是这样。

为了补充Alf的答案,当您不调用
tweak()
时,您不会得到复制操作,这是因为标准允许(但不要求)省略对复制构造函数的调用来创建临时异常对象。来自C++03 15.1/5(引发异常):

如果可以使用临时对象 在不改变环境的情况下被淘汰 除以下内容外,本计划的含义: 施工人员和承包商的执行 与使用 临时对象(12.2),然后 处理程序中的异常可以是 直接用参数初始化 扔的表情。当 抛出的对象是类对象,并且 复制构造函数用于 未初始化临时副本 如果可以访问,则程序的格式不正确 (即使临时对象可能 否则将被淘汰)


如果您将复制构造函数设置为私有,gcc将给您一个错误(即使当构造函数是公共的时,它也不会被调用)。MSVC不会给出错误,但我认为应该是这样。

那么您如何修改我的示例以使用clone()和/或虚拟恢复方法呢?@Paul:我不会。但是,在我写这篇文章时,你没有解释你的
tweak
方法要实现什么。猜测一下,它意味着做一些额外的初始化,一个好的方法是定义一个派生类,并在
throw
语句中使用该类。干杯&嗯,我不明白需要一个虚拟的再投掷者。如果您通过引用捕获并使用
throw,一旦实现隐藏了异常,就不会再进行复制。我倾向于对第一行进行向上投票,但同时倾向于对第二行进行向下投票。在很少的情况下,您实际上希望异常是
可克隆的
可重试的
。在一般情况下,通过
抛出可以很容易地获得重新抛出
在异常处理程序中没有参数,并且
克隆也没有用处。(这两种情况的唯一情况是,在没有C++的情况下,在一个线程中捕获并重新抛出另一个线程。
my_exception(): this=7fff5fbfeae0
my_exception( my_exception const& )
~my_exception()
&e=1001000f0
~my_exception()
my_exception(): this=1001000f0
&e=1001000f0
~my_exception()