【问题标题】:Temporary object not getting created when exception is caught by reference?引用捕获异常时未创建临时对象?
【发布时间】:2012-07-07 05:50:28
【问题描述】:
class Error1
{

public:

int errorcode;
Error1(int x):errorcode(x){ cout<<"CTOR Error1"<<endl; }
//Error1(Error1& obj ){
//    errorcode = obj.errorcode;
//    cout<<"CopyCTOR Error1"<<endl;
//}
~Error1(){cout<<"DTOR Error1"<<endl; }
};

void fun()
{
cout<<"Inside fun"<<endl;
throw(Error1(5));
}

int main()
{
try{

    fun();
}
catch(Error1& eobj)
{
    cout<<"Error1 type occured with code:"<<eobj.errorcode<<endl;
}
cin.get();

}

输出:

Inside fun

CTOR Error1

DTOR Error1

Error1 type occured with code:5

DTOR Error1

此输出表明 Error1 对象是为 catch 处理程序复制构造的。由于没有为 Error1 对象定义复制构造函数,因此使用了默认复制构造函数。

当我取消注释用于定义复制构造函数的注释部分时,我得到以下输出。

Inside fun

CTOR Error1

Error1 type occured with code:5

DTOR Error1

为什么只有一个 DTOR 被调用?即使异常被引用捕获,我相信仍然会创建一个临时的。

【问题讨论】:

  • 看起来输出取决于编译器和异常运行时实现。例如,gcc 4.3.4 不会为您的第一个代码示例使用默认的复制构造函数生成任何临时文件。你可以在这里看到一个例子:ideone.com/9QyLf
  • 首先,如果您将 DTOR 定义为虚拟,您将获得与第二个相同的第一个行为。
  • @klement:这里没有发生多态行为,所以我相信使用virtual DTOR's 是不必要的
  • @Jason:我已经安排了一个实验,我在 VS2010 中得到的结果 - virtual 使两个代码部分都能正常工作。
  • @klement:在 gcc 中,我得到相同的结果,而无需添加 virtual ... 所以再次,使用 virtual 关键字可能是 VS2010 特有的怪癖,但它是“不必要的” C++ 语言一般适用于这个特定场景...

标签: c++ visual-c++ exception exception-handling


【解决方案1】:

你用的是什么编译器?

当您使用 Error1&amp; obj 参数引入(即取消注释)您的复制构造函数版本时,代码应该变得无效。 throw 应该能够创建其参数的副本,而您的复制构造函数版本会禁用临时对象的复制。代码格式不正确。如果您的编译器接受它,可能是因为它非法允许绑定非常量引用到临时对象(我怀疑它是启用了扩展的 MSVC++ 编译器)。

原始实验按预期/允许的方式进行。 throw 的参数被复制到一个内部临时文件,稍后用于初始化catch 参数。虽然允许编译器直接使用你原来的临时文件,但相应地延长了它的生命周期。

【讨论】:

  • 如果他将其定义为私有,它将变得无效。他仍然可以依赖默认副本 CTOR。
  • @klement:来自 OP 的用户定义 CTOR 采用非 const 引用类型,这意味着它不能接受临时参数作为参数 ...
  • @klement:首先,声明任何复制构造函数都会禁用编译器提供的构造函数(这可能是您所说的“默认”)。其次,只要引用参数是非常量,即使使用公共复制构造函数,代码也是无效的。正如我已经说过的,这样的复制构造函数不能用于复制临时对象。
  • 对不起,不知何故误读了您的回答。我以为你是说如果我们注释掉复制的 CTOR 代码就无效了。
  • @rocky:请注意,throw/catch 已经具备一些解决语言限制的能力。例如,您可以使用throw 5catch (int &amp;)。 IE。它允许您将非常量引用绑定到临时对象。这已经是一项了不起的壮举,在该语言的其他任何地方都被禁止。也许即使源对象是 const 也执行临时消除也是合法的……但我不确定。
【解决方案2】:

可能还有其他错误,但我现在看到的是throw(Error1(5)); 创建了一个Error1 类型的临时(或rvalue)。你想要一个lvalue,这意味着你应该要么做throw(*new Error1(5));(我相信这会造成内存泄漏,但我可能错了),或者你可以创建一个全局Error1对象然后扔掉它。

PS:如果有人想发表评论,我很想知道throw(*new Error1(5)); 是否会造成内存泄漏。 catch 会破坏它捕获的对象吗?如果是这样,我认为您应该可以在需要时创建新的Error1s。

【讨论】:

  • 你的收获是什么?您正在抛出取消引用的指针吗?所以如果你的 catch 像 catch(Error1 obj) 或 catch(Error1& obj) 会导致内存泄漏。
  • 如果你手动删除了Error1,它还会泄漏内存吗?
  • 我相信你不能手动删除,除非你通过指针抛出并通过指针捕捉
  • 我不知道你能做到这一点。我稍后会尝试,谢谢。
  • 不是一个好主意...如果您曾经编写过其他人将使用的代码来抛出这样的动态分配的对象,按照惯例,他们很可能会编写采用左值引用的catch 语句,并且您将 100% 保证有泄漏的代码。创建异常运行时是为了处理临时异常对象,因此请按照设计的方式使用系统,不要试图超越它(以及其他任何使用您的代码的人)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多