抛出的对象复制结构 - 为什么?

时间:2011-03-22 06:18:00

标签: c++ exception-handling

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

3 个答案:

答案 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()返回对此本地对象的引用。然后,当您抛出此引用时,您将超出范围并删除本地对象。因此,实现将类复制到动态内存以保持引用有效。

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

相关问题