【问题标题】:Difference between try-catch syntax for function函数的 try-catch 语法之间的区别
【发布时间】:2011-10-09 01:45:56
【问题描述】:

我最近遇到了try-catch for function 的这种语法。

struct A
{
  int a;

  A (int i) : a(i)  // normal syntax
  {
    try {}
    catch(...) {}
  }

  A ()   // something different
  try : a(0) {}
  catch(...) {}

  void foo ()  // normal function
  try {}
  catch(...) {}
};

syntax are valid。除了编码风格之外,这些语法之间是否有任何技术差异?其中一种语法在任何方面都优于其他语法吗?

【问题讨论】:

  • 它们编译的事实并不意味着它们是有效的。可能是一个扩展。无论如何,+1 非常有趣的语法 - 以前从未见过 :)
  • @VJo:那是标准 C++,不推荐,但标准。
  • 关于这个话题有一个有趣的故事:gotw.ca/gotw/066.htm...
  • @Als,我明白了,它在 cmets 中,我没看到...

标签: c++ syntax try-catch function-try-block


【解决方案1】:

“something different”示例将初始化列表的处理置于 try 块的范围内。

【讨论】:

  • 如果那是唯一的区别。显着的区别是不可能访问构造函数 function-try-block 中的对象成员,并且在 catch 结束时会重新抛出原始异常(除非注意)。
【解决方案2】:

第一种语法:
try 块的范围在成员初始化列表完成后开始,因此在成员初始化期间抛出的任何异常都不会被该 try-catch 块捕获。

第二种语法:
它确保如果在成员初始化列表期间引发异常,则您能够捕获该异常。

第三种语法:
它确保从函数体内的 try 块的起始大括号之间抛出的任何异常都被适当地捕获,这意味着在参数传递期间引起的任何异常(如果可能发生)都不会在这个 try-catch 块中被捕获。

所以是的,它们提供的功能明显不同。


编辑:
在构造函数和析构函数中使用第二种语法(function-try-block)时需要考虑的一些准则:

根据 C++ 标准,

如果 catch 块没有抛出(重新抛出原始异常,或者抛出新的异常),并且控制到达构造函数或析构函数的 catch 块的末尾,则原始异常会自动重新抛出。

简单来说:
构造函数或析构函数-try-block 的处理程序代码必须通过发出一些异常来结束。

准则 1:
构造函数尝试块处理程序只有一个目的 - 转换异常。 (也许是为了做日志记录或其他一些副作用。)它们对任何其他目的都没有用。

从析构函数中抛出异常是个坏主意,请查看 here 了解原因。
准则 2:
析构函数功能尝试块根本没有实际用途。他们永远不应该检测到任何东西,即使因为恶意代码而检测到某些东西,处理程序也没有什么用处,因为它无法抑制异常。

准则 3:
始终在构造函数或析构函数体中的本地 try-block 处理程序中清理非托管资源获取,而不是在构造函数或析构函数 function-try-block 处理程序中。


对于标准爱好者:

C++ 标准,第 15.3 条,第 15 段:

如果 return 语句出现在构造函数的 function-try-block 的处理程序中,则程序格式错误。

C++ 标准,第 15.3 条,第 16 段:

如果控制到达构造函数或析构函数的函数尝试块的处理程序的末尾,则重新抛出正在处理的异常。否则,当控制到达 function-try-block (6.6.3) 的处理程序末尾时,函数返回。从函数尝试块的末尾流出相当于没有值的返回;这会导致值返回函数 (6.6.3) 中的未定义行为。


参考资料:
看看这个必读资源here了解更多详情和解释。

【讨论】:

  • 第三个,你的意思是在参数传递期间引起的异常被捕获(你目前有“不会被捕获”)。
  • 解释不完整到不正确的地步。
  • @Matt Smith:try 块从 try 的左大括号开始,所以我的意思正是我所说的。
  • @Als:我相信如果你没有在catch 中返回,函数 try/catch 块有一个特定的行为,那就是重新抛出异常。我可能只适用于析构函数......不确定。
  • @Matthieu M. §15.3/15 如果 return 语句出现在 constructor的 function-try-block 的处理程序中>,程序格式错误 §15.3/16 如果控制到达函数try-的处理程序的末尾,则重新抛出正在处理的异常构造函数或析构函数的块。否则,当控制到达 function-try-block (6.6.3) 的处理程序末尾时,函数返回。从函数尝试块的末尾流出相当于没有值的返回;这会导致值返回函数 (6.6.3) 中出现未定义的行为。
【解决方案3】:

不妨引用规范...或者至少是draft

第 15 (4) 节:

function-try-blockhandler-seqctor-initializer(如果存在)和 compound 相关联-声明。在执行复合语句期间抛出的异常,或者对于构造函数和析构函数,分别在类的子对象的初始化或销毁期间,将控制权转移到function-try中的处理程序-block 以与执行 try-block 期间抛出的异常相同的方式将控制权转移给其他处理程序。

(这里的 handler-seqcatch 之后的东西,compound-statement 是函数体。)

因此,构造函数或析构函数上的“函数尝试块”会捕获 ctor-initializers 以及子对象的构造或销毁引发的异常。

在构造函数或析构函数以外的函数上,它与简单地包装函数体相同。 (好吧,据我从阅读规范中可以看出。)

有趣的功能,对我来说是新的。感谢您提出。

【讨论】:

  • 捕获由 ctor-initializers 和子对象的构造或销毁所引发的异常 - 这是一种轻描淡写的说法。
  • 在除了构造函数或析构函数之外的函数上,它与简单地包装函数体是一样的——绝对不是!
  • @Gene:解释一下?您自己的回答说“在正常功能中,此功能没有用”。 (请注意,您如此巧妙地提到的“自动重新抛出”行为仅发生在收缩器和析构器中。)那么,解释一下?
  • @Nemo:我在上面的回答中包含了关于正常功能的评论。
【解决方案4】:

Function-try-block 主要在构造函数中很有用,因为在初始化列表中没有其他方法可以捕获异常。在析构函数中,必须小心在 catch 块中返回,因为异常会自动重新抛出。 (并且在良好的设计中,析构函数不能抛出。)在普通函数中,这个特性是没有用的。 编辑:一篇旧但仍然很好的文章:http://drdobbs.com/184401316

【讨论】:

  • 有趣的文章,谢谢。请记住,SO 政策是通过总结文章来对抗超链接腐烂。只是为了在链接失效时您的答案不会变得毫无意义。
  • @Matthieu:很好的规则,但有两个问题:摘要通常比原始内容差得多,而且......我只是太懒了:-)
  • 那么你的答案注定会停留在没人会阅读的底部:/太糟糕了,因为链接很有趣。
  • 只有在构造函数或析构函数中有 function-try-block 的情况下才会重新抛出异常。在其余函数中,其行为与 try-block 完全相同,该 try-block 将所有其他代码包含在函数中。
  • @David Rodríguez:感谢您更正 re-throwing 部分。但它仍然和普通的try-block不完全一样,你不能从catch中访问函数体中的局部变量,因为它们都在try-block中。所以我看不出 function-try-block 在普通函数中可能有用的地方,普通 try-block 总是更好的解决方案。
猜你喜欢
  • 1970-01-01
  • 2011-02-20
  • 2012-01-14
  • 2010-12-14
  • 2014-04-14
  • 1970-01-01
  • 1970-01-01
  • 2012-01-17
  • 1970-01-01
相关资源
最近更新 更多