函数try块何时有用?

Naw*_*waz 36 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;
}
Run Code Online (Sandbox Code Playgroud)

输出:(在ideone处)

greater than zero
less than zero
Run Code Online (Sandbox Code Playgroud)

编辑:因为有些人可能认为函数定义的语法不正确(因为语法看起来不熟悉),我要说它不是不正确的.它叫做function-try-block.参见C++标准中的§8.4/ 1 [dcl.fct.def].

Ale*_* C. 27

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

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

void f() try { ... } catch (...) { ... }
Run Code Online (Sandbox Code Playgroud)

严格等同于

void f() { try { ... } catch (...) { ... } }
Run Code Online (Sandbox Code Playgroud)

  • @akavel:他们没有用武之地.您可能希望在重新引发异常之前执行一些清理或日志记录.或者您可能想要抛出另一个异常(允许).实际上,行为是可以理解的:如果子对象或基础未能构造,则对象无法成功构建并且必须抛出. (14认同)
  • ***警告***:在构造函数和析构函数中,例外情况仍然是***在catch结束之后_implicitly_,因此它们仍然几乎没用 - 请参阅例如:http://www.informit.com/guides /content.aspx?g=cplusplus&seqNum=496 (6认同)
  • `构造函数,initializer`.初始化列表是另一回事. (3认同)
  • @akavel您可以从析构函数的catch块“返回”,在这种情况下,不会重新抛出异常,因为正确销毁对象的目标被认为已完成。对于构造函数,我同意 Alexandre C. 的评论。 (2认同)

hka*_*ser 12

函数try块在两个上下文中对我有用.

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

int main()
try {
    // ...
    return 0;
}
catch (...) {
    // handle errors
    return -1;
}
Run Code Online (Sandbox Code Playgroud)

这显然只是在内部尝试/捕获的语法糖main().

b)处理基类构造函数抛出的异常:

struct B {
     B() { /*might throw*/ }
};

struct A : B {
     A() 
     try : B() { 
         // ... 
     } 
     catch (...) {
         // handle exceptions thrown from inside A() or by B() 
     } 
};
Run Code Online (Sandbox Code Playgroud)

  • @Tomalak:构造函数的`catch`块总是抛出或重新抛出.在它的末尾添加了一个隐含的`throw;`.所以,即使@hkaiser写了这个,也并不意味着该对象是有效的. (9认同)
  • (a)糖通常是甜的.我认为那是"甜蜜的".(b)基类施工失败通常应导致派生类建设失败,如果未被捕获的例外不表示防止基类建设的例外情况,那么你就是滥用例外; 这是代码味道IMO. (6认同)
  • @hkaiser:如果您的基础对象抛出但您仍然可以在"有效状态"中拥有派生对象,我相信您正在不恰当地使用继承.如果基础对象未处于有效状态,则派生对象不能处于有效状态.(注意,*对象*不是*类*.) (4认同)
  • 有时你不关心构造函数抛出的内容; 你只想要*任何*异常被捕获然后抛出一个*已知*异常,因为上游有一个处理程序,需要区分"我的构造函数由于某种原因抛出"与"我的其他一些代码扔掉",阻止它使用catch -所有. (4认同)
  • @Tomalak:我同意你的看法:我接受这样一个构造的唯一有效案例是你从第三方类继承,它抛出一个不在你树中的异常,并且你想要转换.每个其他情况(成员对象构造错误也是如此)闻起来. (3认同)

kal*_*axy 8

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

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

void f(/*...*/)
try {
   /*...*/
}
catch(/*...*/) {
    /*...*/
}
Run Code Online (Sandbox Code Playgroud)

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

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

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


Vit*_*tus 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";
  }
}
Run Code Online (Sandbox Code Playgroud)


nob*_*bar 6

关于函数 try 块如何操作的注意事项:

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

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

  • 构造函数或析构函数中的 catch 块必须抛出一些异常(否则它会隐式地重新抛出捕获的异常)。简单地return(至少在构造函数的函数 catch 块中)是不合法的。但是请注意,您可以调用exit()或类似的方法,这在某些情况下可能是有意义的。

  • catch 块不能返回值,因此它不适用于返回非 void 的函数(除非它们故意用exit()或类似的方式终止程序)。至少这是我读过的

  • 构造函数尝试的 catch 块不能引用数据/基成员,因为它们将 1) 未构造或 2) 在捕获之前被破坏。因此,函数 try 块对于清理对象的内部状态没有用——当你到达那里时,对象应该已经完全“死了”。 这个事实使得在构造函数中使用函数 try 块变得非常危险,因为如果你的编译器没有标记它,随着时间的推移很难监管这个规则。

有效(合法)用途

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


Jus*_*ica 5

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

// 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 */
Run Code Online (Sandbox Code Playgroud)

这将使您既可以在测试代码时获得有用的信息,又可以轻松地对其进行模拟,而不会影响任何内容。