[英]When is a function try block useful?
我想知道程序员何时使用函数try块。 什么时候有用?
void f(int i)
try
{
if ( i < 0 )
throw "less than zero";
std::cout << "greater than zero" << std::endl;
}
catch(const char* e)
{
std::cout << e << std::endl;
}
int main() {
f(1);
f(-1);
return 0;
}
输出:(在ideone处 )
greater than zero
less than zero
编辑:正如某些人可能认为函数定义的语法不正确(因为语法看起来不熟悉),我不得不说没有它不正确。 它称为功能尝试块。 请参阅C ++标准中的第8.4 / 1节[dcl.fct.def]。
您可以在构造函数中使用它来捕获初始化程序的错误。 通常,您不会捕获这些错误,因此这是一种非常特殊的用法。
否则,它是没有用的:除非我被证明是错误的,
void f() try { ... } catch (...) { ... }
严格等于
void f() { try { ... } catch (...) { ... } }
在两个上下文中,功能try块对我很有用。
a)在main()
周围有一个catch子句,允许编写小型实用程序,而不必担心本地错误处理:
int main()
try {
// ...
return 0;
}
catch (...) {
// handle errors
return -1;
}
显然,这只是在main()
本身内部进行try / catch的语法糖。
b)处理基类构造函数引发的异常:
struct B {
B() { /*might throw*/ }
};
struct A : B {
A()
try : B() {
// ...
}
catch (...) {
// handle exceptions thrown from inside A() or by B()
}
};
除了提到的功能用途,您还可以使用function-try-block来节省一级缩进。 (Ack,有关编码样式的答案!)
通常,您会看到带有function-try-block的示例,如下所示:
void f(/*...*/)
try {
/*...*/
}
catch(/*...*/) {
/*...*/
}
如果将功能范围缩进到与没有function-try-block相同的级别。 在以下情况下这可能很有用:
git blame -w
。) 但是,对于完全由功能尝试块包装的功能,我建议不要在使用功能尝试块的某些功能与不在同一代码库中的某些功能之间交替使用。 一致性可能比换行问题更重要。 :)
如果要从构造函数的初始化程序中捕获异常,这可能会很有用。
但是,如果您确实以这种方式在构造函数中捕获异常,则必须重新抛出该异常或引发新的异常(即,您不能仅从构造函数正常返回)。 如果不重新抛出,它只是隐式发生。
#include <iostream>
class A
{
public:
A()
try {
throw 5;
}
catch (int) {
std::cout << "exception thrown\n";
//return; <- invalid
}
};
int main()
{
try {
A a;
}
catch (...) {
std::cout << "was rethrown";
}
}
有关功能try块如何工作的注释:
对于构造函数,功能try块包含数据成员和基类的构造。
对于析构函数,函数try块包含对数据成员和基类的破坏。 它变得复杂,但是对于C ++ 11,您必须在析构函数(或基类/成员类的声明noexcept(false)
的声明中包括noexcept(false)
,否则任何销毁异常将导致catch块结束时终止。 可以通过在catch块中放入return
语句来防止这种情况发生(但是这对构造函数无效)。
构造函数或析构函数中的catch块必须抛出一些异常(否则它将隐式地重新抛出捕获的异常) 。 仅return
(至少在构造函数的catch块中)是不合法的。 但是请注意,您可以调用exit()
或类似方法,这在某些情况下可能有意义。
catch块无法返回值,因此不适用于返回非void的函数(除非它们有意用exit()
或类似方法终止程序)。 至少那是我读过的 。
构造函数尝试的catch块不能引用数据/基本成员,因为它们将在catch之前已被1)未构造或2)被破坏。 这样,函数try块对于清理对象的内部状态没有用-到到达该对象时,该对象应该已经完全“死”了。 这个事实使得在构造函数中使用函数try块非常危险,因为如果您的编译器没有巧合地标记该规则,那么随着时间的推移很难监管该规则。
有效(合法)使用
您可以使用它们的另一件事是在调试过程中以不干扰最终构建的方式提供额外的数据。 我还没有看到其他人使用或提倡使用它,但是我觉得很方便。
// Function signature helper.
#if defined(_WIN32) || defined(_WIN64)
#define FUNC_SIG __FUNCSIG__
#elif defined(__unix__)
#define FUNC_SIG __PRETTY_FUNCTION__
// Add other compiler equivalents here.
#endif /* Function signature helper. */
void foo(/* whatever */)
#ifdef DEBUG
try
#endif /* DEBUG */
{
// ...
}
#ifdef DEBUG
catch(SomeExceptionOrOther& e) {
std::cout << "Exception " << e.what() << std::endl
<< "* In function: " << FUNC_SIG << std::endl
<< "* With parameters: " << /* output parameters */ << std::endl
<< "* With internal variables: " << /* output vars */ << std::endl;
throw;
}
#endif /* DEBUG */
这将使您既可以在测试代码时获得有用的信息,又可以轻松地将其虚拟化而不会影响任何内容。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.