我最近为try-catch
函数找到了这种语法。
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。除了编码风格之外,这些语法之间是否有任何技术差异?在任何方面,语法之一是否优于其他语法?
答案 0 :(得分:45)
第一句法:
成员初始化列表完成后,try块的范围开始,因此在此初始化期间抛出的任何异常都不会被此try-catch块捕获。
第二种语法:
它确保如果在Member Initialization列表中抛出异常,那么您就能捕获异常。
第三种语法:
它确保在函数体内的try块的起始括号之间抛出的任何异常都被恰当地捕获,这意味着在参数传递期间引起的任何异常(如果有的话)都不会被捕获到这个try-catch块中。
所以是的,他们在提供的功能方面完全不同。
修改强>
在构造函数中使用第二种语法(function-try-block)时要考虑的一些准则&析构者:
根据C ++标准,
如果catch块没有抛出(重新抛出原始异常,或抛出新的东西),并且控制到达构造函数或析构函数的catch块的末尾,则原始异常会自动重新抛出。
用简单的话说:
构造函数或析构函数function-try-block的处理程序代码必须通过发出一些异常来完成。
准则1:
构造函数 - try-block处理程序只有一个目的 - 转换异常。 (也许是为了记录或其他一些副作用。)它们对任何其他目的都没用。
从析构函数中抛出异常是个坏主意,看看 here 就知道原因了。
准则2:
析构函数 - try-blocks根本没有实际用途。它们永远不应该有任何东西可供检测,即使由于恶意代码而有某些东西需要检测,处理程序也不会对它做任何事情非常有用,因为它无法抑制异常。
准则3:
始终在构造函数或析构函数体内的本地try-block处理程序中清理非托管资源获取,从不在构造函数或析构函数中使用try-try-block处理程序。
对于Standardese粉丝:
C ++标准,第15.3条,第15段:
如果返回语句出现在构造函数的function-try-block的处理程序中,则程序格式错误。
C ++标准,第15.3条,第16段:
如果控制到达构造函数或析构函数的function-try-block的处理程序的末尾,则会重新抛出正在处理的异常。否则,当控制到达function-try-block(6.6.3)的处理程序的末尾时,函数返回。流出函数try-block的末尾相当于没有值的返回;这导致值返回函数(6.6.3)中的未定义行为。
<强>参考文献:强>
请查看必读资源 here 以获取更多详细信息。解释
答案 1 :(得分:9)
Function-try-block 主要在构造函数中很有用,因为在初始化列表中没有其他方法可以捕获异常。在析构函数中,必须小心返回catch块,因为异常将被自动重新抛出。 (并且在良好的设计中,析构函数不能抛出。)在正常函数中,此功能无用。 编辑:一篇陈旧但仍然很好的文章:http://drdobbs.com/184401316
答案 2 :(得分:3)
也可以引用规范......或者至少是draft。
第15(4)条:
function-try-block 将 handler-seq 与 ctor-initializer (如果存在)和化合物相关联语句来。执行复合语句期间抛出的异常,或者对于构造函数和析构函数,在类的子对象的初始化或销毁期间,将控制权转移到 function-try中的处理程序-block 与执行 try-block 期间抛出的异常相同,将控制转移给其他处理程序。
(此处 handler-seq 是catch
之后的内容,compound-statement
是函数体。)
因此,构造函数或析构函数上的“function try block”会捕获ctor-initializers引发的异常以及子对象的构造或破坏。
在构造函数或析构函数以外的函数上,它与简单包装函数体相同。 (好吧,据我所知,从阅读规范中可以看出。)
有趣的功能,对我来说很新。谢谢你提出来。
答案 3 :(得分:2)
“不同的东西”示例将初始化列表的处理放在try块的范围内。