函数try块的目的是什么?

Bel*_*loc 9 c++ exception-handling exception function-try-block

可能重复:
函数try块何时有用?
函数的try-catch语法之间的区别

此代码int在构造Dog类中的对象时抛出异常UseResources.该int异常由正常捕获try-catch块和代码输出:

Cat()  
Dog()  
~Cat()  
Inside handler
Run Code Online (Sandbox Code Playgroud)

#include <iostream>
using namespace std;

class Cat
{
    public:
    Cat() { cout << "Cat()" << endl; }
    ~Cat() { cout << "~Cat()" << endl; }
};

class Dog
{
    public:
    Dog() { cout << "Dog()" << endl; throw 1; }
    ~Dog() { cout << "~Dog()" << endl; }
};

class UseResources
{
    class Cat cat;
    class Dog dog;

    public:
    UseResources() : cat(), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { cout << "~UseResources()" << endl; }
};

int main()
{
    try
    {
        UseResources ur;
    }
    catch( int )
    {
        cout << "Inside handler" << endl;
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,如果我们替换UseResources()构造函数的定义,使用一个function try block,如下所示,

UseResources() try : cat(), dog() { cout << "UseResources()" << endl; } catch(int) {}
Run Code Online (Sandbox Code Playgroud)

输出是一样的

Cat()  
Dog()  
~Cat()  
Inside handler 
Run Code Online (Sandbox Code Playgroud)

即,具有完全相同的最终结果.

那么,一个目的是function try block什么?

Nic*_*las 12

想象一下,如果UseResources定义如下:

class UseResources
{
    class Cat *cat;
    class Dog dog;

    public:
    UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { delete cat; cat = NULL; cout << "~UseResources()" << endl; }
};
Run Code Online (Sandbox Code Playgroud)

如果Dog::Dog()抛出,那么cat会泄漏内存.Becase UseResources的构造函数从未完成,对象从未完全构建.因此它没有被称为析构函数.

要防止此泄漏,您必须使用函数级try/catch块:

UseResources() try : cat(new Cat), dog() { cout << "UseResources()" << endl; } catch(...)
{
  delete cat;
  throw;
}
Run Code Online (Sandbox Code Playgroud)

为了更全面地回答你的问题,构造函数中的函数级try/catch块的目的是专门进行这种清理.函数级try/catch块不能吞下异常(常规异常).如果他们捕获了某些东西,当他们到达catch块的末尾时,他们会再次抛出它,除非你明确地重新抛出它throw.您可以将一种类型的异常转换为另一种类型,但是您不能只是吞下它并继续前进,就像它没有发生一样.

这是为什么应该使用值和智能指针而不是裸指针的另一个原因,即使是作为类成员.因为,在您的情况下,如果您只有成员值而不是指针,则不必执行此操作.它是使用裸指针(或在RAII对象中未管理的其他形式的资源)来强制这种事情.

请注意,这几乎是函数try/catch块的唯一合法用法.


更多理由不使用函数try块.上面的代码巧妙地破解了.考虑一下:

class Cat
{
  public:
  Cat() {throw "oops";}
};
Run Code Online (Sandbox Code Playgroud)

那么,在UseResources构造函数中会发生什么?好吧new Cat,显然,表达式会抛出.但这意味着cat从未初始化.这意味着delete cat将产生未定义的行为.

您可以尝试使用复杂的lambda而不仅仅是new Cat:

UseResources() try
  : cat([]() -> Cat* try{ return new Cat;}catch(...) {return nullptr;} }())
  , dog()
{ cout << "UseResources()" << endl; }
catch(...)
{
  delete cat;
  throw;
}
Run Code Online (Sandbox Code Playgroud)

理论上解决了这个问题,但它打破了假定的不变量UseResources.也就是说,它UseResources::cat始终是一个有效的指针.如果那确实是一个不变量UseResources,则此代码将失败,因为它允许构建UseResources尽管异常.

基本上,除非new Catnoexcept(显式或隐式),否则无法使此代码安全.

相比之下,这始终有效:

class UseResources
{
    unique_ptr<Cat> cat;
    Dog dog;

    public:
    UseResources() : cat(new Cat), dog() { cout << "UseResources()" << endl; }
    ~UseResources() { cout << "~UseResources()" << endl; }
};
Run Code Online (Sandbox Code Playgroud)

简而言之,将功能级别的try-block视为严重的代码味道.

  • @ user1042389:不,它不会.如果它是一个函数级的`catch`,`catch`只会自动重新抛出异常.*函数中的`catch`*可以选择是否重新抛出异常.如果没有,则在`catch`块结束后继续执行. (2认同)
  • @NicolBolas我相信您的代码并不安全:如果`new Cat`抛出异常,那么catch块也将被执行。在那里,您对包含垃圾的指针调用了delete。该指针可能包含有效的内存地址,这将导致严重的程序崩溃。 (2认同)