函数try块何时有用?

时间:2011-04-10 14:41:35

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

我想知道程序员何时使用函数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
编辑:有些人可能认为函数定义的语法不正确(因为语法看起来并不熟悉),我要说它不是不正确的。它叫做function-try-block。参见C ++标准中的§8.4/ 1 [dcl.fct.def]。

6 个答案:

答案 0 :(得分:26)

您在构造函数中使用它来捕获初始化程序中的错误。通常,您没有捕获这些错误,因此这是非常特殊的用途。

否则,它是无用的:除非我被证明是错误的,

void f() try { ... } catch (...) { ... }

严格等同于

void f() { try { ... } catch (...) { ... } }

答案 1 :(得分:11)

在两种情况下,函数try块对我很有用。

a)在main()周围有一个catch all子句,允许编写小实用程序,而不必担心本地错误处理:

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() 
     } 
};

答案 2 :(得分:8)

除了提到的功能用途之外,您还可以使用function-try-block来保存一级缩进。 (Ack,关于编码风格的答案!)

通常你会看到如下函数try-block的例子:

void f(/*...*/)
try {
   /*...*/
}
catch(/*...*/) {
    /*...*/
}

其中函数作用域缩进到与没有函数try-block相同的级别。这在以下情况下非常有用:

  • 你有80个字符的列限制,并且必须在给定额外缩进的情况下换行。
  • 您正尝试使用try catch改进一些现有功能,并且不想触及该功能的所有行。 (是的,我们可以使用git blame -w。)

但是,对于完全用函数try-block包装的函数,我建议使用function-try-blocks在某些函数之间交替,而不是在同一代码库中。一致性可能比换行问题更重要。 :)

答案 3 :(得分:6)

如果要从构造函数的初始值设定项中捕获异常,这可能很有用。

但是,如果你以这种方式在构造函数中捕获异常,那么要么重新抛出它,要么抛出新的异常(即你不能正常地从构造函数返回)。如果你不重新抛出,它就会隐含发生。

#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";
  }
}

答案 4 :(得分:3)

有关函数try块如何运行的注释:

  • 对于构造函数,函数try块包含数据成员和基类的构造。

  • 对于析构函数,函数try块包含数据成员和基类的销毁。它变得复杂,但是对于C ++ 11,你必须在析构函数的声明(或基类/成员类的声明)中包含noexcept(false),否则任何销毁异常将导致在catch的结束时终止块。可以通过在catch块中放置return语句来防止这种情况(但这对构造函数不起作用)。

  • 构造函数或析构函数中的catch块必须抛出一些异常(or it will implicitly rethrow the caught exception)。简单地return(至少在构造函数的函数catch块中)是不合法的。但请注意,您可以拨打exit()或类似内容,这在某些情况下可能有用。

  • 一个catch块不能返回一个值,因此它对返回非void的函数不起作用(除非它们故意用exit()或类似的方式终止程序)。至少是what I've read

  • 构造函数-function-try的catch块不能引用数据/基本成员,因为它们要么具有1)未构造,要么2)在catch之前被破坏。因此,函数try块对于清理对象的内部状态没有用 - 对象应该已经完全“死了”#34;当你到达那里的时候。 这一事实使得在构造函数中使用函数try块非常危险,因为如果您的编译器没有碰巧标记它,很难随时监控此规则。

有效(合法)使用

  • 翻译构造函数或其基础/成员构造函数期间抛出的异常(到不同的类型/消息)。
  • 在析构函数或它的基础/成员析构函数(尽管destructor etiquette)期间转换或吸收和抛出异常。
  • 终止程序(可能带有有用的消息)。
  • 某种异常日志记录方案。
  • 用于返回void的函数的语法糖,恰好需要一个完全封装的try / catch块。

答案 5 :(得分:2)

您可以使用它们的另一件事是在调试期间以不干扰已完成构建的方式提供额外数据。我没有看到其他人使用或提倡它,但这是我觉得方便的东西。

// 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 */

这将允许您在测试代码时获取有用的信息,并轻松地将其伪装而不会影响任何内容。