2011-12-02 115 views
8

可能重复:
When is a function try block useful?
Difference between try-catch syntax for function函数try块的用途是什么?

在构造内UseResourcesDog对象时,此代码引发int异常。如下

Cat() 
Dog() 
~Cat() 
Inside handler 

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

现在,如果我们更换UseResources()构造函数的定义,与使用function try block之一:该int异常由正常try-catch块和代码输出捕获,

UseResources() try : cat(), dog() { cout << "UseResources()" << endl; } catch(int) {} 

的输出是相同的

Cat() 
Dog() 
~Cat() 
Inside handler 

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

那么什么是function try block的目的呢?

+4

重复的副本重复... – Xeo

+0

这是C++ 11吗?我从来没有见过它 –

+0

@VJovic我不知道这是什么时候在语言中引入的。但它并不新鲜。 – Belloc

回答

9

试想一下,如果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; } 
}; 

如果Dog::Dog()抛出,然后cat将导致内存泄漏。因为UseResources的构造函数从来没有完成,该对象从未完全构建。因此它没有调用析构函数。

为了防止这种泄漏,必须使用函数级别try/catch块:

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

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

这是为什么应该使用值和智能指针而不是裸指针(即使是类成员)的另一个原因。因为,就像你的情况一样,如果你只有成员值而不是指针,你不必这样做。这是使用裸指针(或RAII对象中未管理的其他形式的资源)强制这种事情。

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


更多的原因不使用函数try块。上面的代码被细微地破坏了。试想一下:

class Cat 
{ 
    public: 
    Cat() {throw "oops";} 
}; 

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

您可以尝试通过使用复杂的λ,而不是仅仅new Cat纠正这一点:

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

理论上解决了这个问题,但它打破了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; } 
}; 

总之,看起来功能级别的试块为严重码味上。

+0

有趣。目前为止我没有看到过使用函数尝试阻止这个清理事件的例子。但在你的例子中,程序无论如何都会中止,那么这个特性有什么大不了的?无论如何,如果操作系统正在进行清理,那么清理的目的是什么? – Belloc

+0

异常处理不是关于如何处理失败,而是更优雅地处理失败。此功能可以帮助您优雅地释放资源。一个实际的例子可能是客户端崩溃,并使用它来释放服务器资源,使其不会让其他客户端无法使用。 – AJG85

+0

@ AJG85但是'terminate()'将总是被一个函数try块调用,在这种情况下,任何资源都将被操作系统释放。这不正确吗? – Belloc

2

普通函数try块的用途相对较小。他们几乎等同于身体内部try块:

int f1() try { 
    // body 
} catch (Exc const & e) { 
    return -1; 
} 

int f2() { 
    try { 
    // body 
    } catch (Exc const & e) { 
    return -1; 
    } 
} 

唯一的区别是,函数试块住在稍大功能范围,而第二建筑居住在函数体-scope - 前一个范围只能看到函数参数,后者也是局部变量(但这不会影响try块的两个版本)。

唯一有趣的应用是在一个构造 - 尝试块:

Foo() try : a(1,2), b(), c(true) { /* ... */ } catch(...) { } 

这是从初始化的一个例外可以被捕获的唯一途径。您不能处理异常,因为整个对象构造必须仍然失败(因此无论您是否需要,您都必须退出catch块,并发出异常)。但是,它专门处理来自初始化程序列表的异常的唯一方法。

这个有用吗?可能不会。 有本质上是一个构造函数try块及以下,较为典型的“初始化到零和分配”的格局,没有区别它本身是可怕的:

Foo() : p1(NULL), p2(NULL), p3(NULL) { 
    p1 = new Bar; 
    try { 
    p2 = new Zip; 
    try { 
     p3 = new Gulp; 
    } catch (...) { 
     delete p2; 
     throw; 
    } 
    } catch(...) { 
    delete p1; 
    throw; 
    } 
} 

正如你所看到的,你有一个不可维护,不可扩展的混乱。构造函数尝试块会更糟糕,因为你甚至无法知道已经分配了多少指针。所以真的是只有有用,如果你有两个可泄漏的分配。更新:感谢您阅读this question我被告知实际上您根本无法使用catch块清理资源,因为引用成员对象是未定义的行为。所以[结束更新]

总之:没用。

+0

“这是没用的。”除非你想翻译例外。 –

相关问题