2010-01-31 89 views
2

什么是更好的编码实践:如果我必须有一个try/catch块,我应该在这个块中放置一切(每次初始化等)还是只放置那些可能抛出的变量?这两种结构有什么不同?尝试全部或仅需要什么?

在例如:
具有:

struct A { 
    A(); 
    int a; 
    int* b; 
};  
中的.cpp

和稍后:

A::A() { 
    a = 5; 
    try { 
     b = new int; 
    } 
    catch(...){ 
    } 
} 

A:A() { 
    try { 
    a = 5; //this time in try block 
     b = new int; 
    } 
    catch(...) { 
    } 
} 

是有这两个构建体或是之间的任何差异这在某种程度上,如果我必须有一个try/catch块,我可能会永远放弃什么东西在里面? 谢谢。

P.S. 对于上帝efin的缘故,这里格式化让我真的很疯狂!而且我知道我多次提到过,我不会生气。

+0

纠正我,如果我错了:唯一的区别是把a = 5;在try语句中,是吗? – dgraziotin 2010-01-31 20:33:00

+4

我已经修复了格式并减少了垂直空白。格式化代码确实不是太困难 - 使用文本输入区域上方的代码格式化按钮。 – 2010-01-31 20:35:59

+0

我想你只是说明一个例子,但除了在主要函数或析构函数中捕获所有元素并不是一个好主意。告诉读者你期待什么样的例外。一个catch(...)告诉读者,你不知道抛出了什么样的异常,或者没有任何异常应该逃脱。 – daramarak 2010-01-31 20:49:49

回答

10

我认为一个很好的一般原则是尽可能使“try”块“尽可能窄” - 不要在其中放入您认为永远不会导致异常的东西。这样,如果你曾经错了,并且其中一个“不会引起异常”的部分实际上是做了会导致异常,你不会意外地“吞食”令人惊讶的异常(并且无疑地在不恰当的情况下处理它方式,因为你的代码不会期望例外,但其他类型)。

+1

+1。 另外,try语句在堆栈上创建一个新的SAR(作用域激活记录)。请阅读有关激活记录的更多信息:http://webster.cs.ucr.edu/AoA/Windows/HTML/IntermediateProceduresa2.html。 作用域激活记录与激活记录相似,但仅将堆栈中的本地值推入尝试括号内,并且包含指向打开它的AR(激活记录)的指针。用不好的话来说,在使用不同的方法时,使用不同的编译器会有不同的范围。将所有内容放在try块中也会降低性能。 – dgraziotin 2010-01-31 20:40:18

3

在你的代码中,我不会在你指明的地方使用try块。内存分配错误实际上非常罕见,并且很难从中恢复。我可能会在main中捕获异常,记录错误并退出程序。更广泛地说,在C++中,你不应该包装每个可能抛出try块的函数。您应该使用熟知的C++习惯用法(如RAII)编写能够处理异常的代码。

+1

我想他正在使用这个例子来表示你可以选择在try/catch块中包装整个方法的所有情况,或者只是捕获几行可能负责抛出异常的代码。在任何情况下,第二种方法都是正确的:) – dgraziotin 2010-01-31 20:42:48

2

除了Alex和Neil的优点之外,狭窄的try块使得代码更容易理解。未来的维护者会更容易知道发生了什么。

+0

+1一个非常好的观点,包含其他内容会表明您期望每一行代码都会抛出某种异常。 – daramarak 2010-01-31 20:45:50

1

@ Alex, 我不同意。您应该将程序的逻辑部分分组为尝试捕获失败或整体传递。如果失败了,即使你没有想到它的代码应该是异常安全的,整个操作应该会失败,所有的资源应该会自动释放。如果你把它们缩小,那么你最终会得到吨和吨的尝试和捕获。为什么不使用IF? (但并不是说第一个例外的地方,避免了很多ifs?)
Parashift也与我一致。
http://www.parashift.com/c++-faq-lite/exceptions.html#faq-17.12
阅读17.12和17.13。

+0

这更多关于编程风格和观点。有人喜欢精确定义“危险”代码块,还有其他人更喜欢将代码看作是逻辑部分,正如您指出的那样。 第一种方法非常精确但冗长且高效(还提供了精确的警报和焦点)。第二种方法是优雅的,使代码块更像数据库中的事务。这个决定也取决于你正在编写的程序类型。对于初学者,我会推荐Alex建议的方法 – dgraziotin 2010-01-31 21:02:13

0

在我的代码中,除了在main()之外,我很少捕捉任何东西。如果你觉得你需要自己清理catch块,你做错了什么,需要看看RAII的成语。至于其他方面 - 只有在特殊情况下才会抛出异常,并且很难想出一些通用的聪明的例外情况。

1

另一种看待这个问题的方法是在try块中包含语句,当他们都需要成功或失败时。这样,catch块就像代码中的一种交易功能。所以,你可以这样做:

// save current state 
try { 
    // a few statements 
    // that each modify the state 
} catch (e) { 
    // rollback to original state 
} 

在你的榜样,分配给成员变量改变对象的状态,所以它应该是try块内。