【问题标题】:Need I Destroy a Thrown Object Manually?需要我手动销毁抛出的对象吗?
【发布时间】:2020-04-06 15:29:28
【问题描述】:

我只是想知道在 C++ 中是否需要手动销毁抛出的对象?

在下面的代码中,如何销毁抛出的0?

try
{
   ...
   throw 0;
}
catch(int i)
{
   // How to destroy the thrown 0?
}

以下代码中,如何销毁抛出的CString对象?

try
{
   ...
   throw CString(_T("Hello"));
}
catch(CString& str)
{
   // How to destroy the thrown str?
}

在下面的代码中,我可以销毁抛出的对象,因为它是作为 CString* 从堆中分配的

try
{
   ...
   throw new CString(_T("Hello"));
}
catch(CString* lpStr)
{
   delete lpStr;
}

【问题讨论】:

  • 在前两个示例中,您不要“破坏”对象。这将作为异常处理的一部分自动发生。
  • 更多信息here

标签: c++ exception try-catch throw


【解决方案1】:

您不需要也不允许手动销毁异常对象或catch 参数。

throw 将从其操作数复制初始化一个新的未命名对象,异常对象,然后该对象被catch 块捕获。 Caught这里的意思是catch块的参数将从异常对象初始化。

catch块退出时,它的参数会被自动销毁,如果它没有重新抛出就退出(并且如果没有std::exception_ptr引用它),异常对象也将被销毁,与对象相同的方式具有自动存储持续时间的对象在声明它们的块退出时被销毁,或者在创建它们的完整表达式结束时销毁临时对象。

在您的第一个代码示例中,int 类型的异常对象使用值 0 进行初始化,并且您按值捕获它,这意味着您正在创建一个 int 类型的新对象作为catch 参数,从异常对象初始化。当catch 块退出时,catch 参数中的int 被销毁,假设您没有重新抛出,异常对象也被销毁。

在第二个示例中,CString 类型的异常对象是从CString(_T("Hello")) 创建的临时对象初始化的。临时的CString(_T("Hello")) 在异常对象初始化后被销毁,因为这是创建它的完整表达式的结尾。

由于复制省略,临时对象永远不会真正实现,CString 异常对象直接从_T("Hello") 初始化。 (自 C++17 起这是强制性的,之前是允许的。)

您通过引用捕获异常对象,这意味着不会创建新副本,str 将引用异常对象。 当离开catch块而不重新抛出时,异常对象将再次被自动销毁。

在第三个示例中,您将抛出 CString* 并按值捕获它,因此适用与示例 1 中相同的注意事项。当离开catch块而不重新抛出时,指针lpStr和同样是CString*的异常对象将被销毁。

但是,销毁指针并不意味着指针指向的对象被销毁。 (这就是为什么不应该将原始指针用作拥有指针的原因。)

因此,由于指针指向使用new 创建的对象,如果您不希望newed CString 泄漏,您确实需要通过调用delete lpStr; 来销毁该对象。这个delete 但是不会删除异常对象或catch 参数delete 销毁指向它的指针指向的对象。 catch参数是指针lpStr本身,是一个自动存储时长变量,不能是deleted。异常对象是另一个指针(具有相同的值)并且未命名,在您的示例中没有任何可能的方式来引用它。

您应该更喜欢在第二个示例中捕获引用,并且应该避免将new 用于任何真正的事情。有智能指针 std::unique_ptrstd::shared_ptr 用于具有正确所有权语义的免费存储分配。

【讨论】:

  • 嗨,@walnut,谢谢你的回答。只是想知道例如 1,我应该像这样捕获 int& 而不是 int 吗?捕捉(int&i)
  • @alancc 对于基本类型,您是按引用还是按值捕获并不重要。但是如果你想保持一致,那么你可以通过引用(例如catch(int& i))来捕获总是(包括int),是的。
猜你喜欢
  • 1970-01-01
  • 2012-06-25
  • 2010-12-31
  • 2011-10-20
  • 2021-11-08
  • 1970-01-01
  • 2021-06-05
  • 2017-05-01
  • 1970-01-01
相关资源
最近更新 更多