功能try块的目的是什么? [英] What is the purpose of a function try block?

查看:136
本文介绍了功能try块的目的是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述


可能重复:

什么时候函数try块有用?

功能的try-catch语法之间的区别


此代码在类 UseResources 中构建 / code>。 int 异常被正常的 try-catch 块捕获,代码输出:

  Cat()
Dog()
〜Cat()
内部处理程序

  #include< iostream> 
使用命名空间std;

class Cat
{
public:
Cat(){cout<< Cat()< ENDL; }
〜Cat(){cout < 〜Cat()< ENDL; }
};

class Dog
{
public:
Dog(){cout<< Dog()<< ENDL;投掷1 }
〜Dog(){cout<< 〜Dog()<< ENDL; }
};

class UseResources
{
class Cat cat;
class狗狗;

public:
UseResources():cat(),dog(){cout<< UseResources()<< ENDL; }
〜UseResources(){cout<< 〜UseResources()<< ENDL; }
};

int main()
{
try
{
UseResources ur;
}
catch(int)
{
cout<<< 内部处理程序<< ENDL;
}
}

现在,如果我们替换 UseResources()构造函数,其中一个使用函数try块,如下所示,

  UseResources()try:cat(),dog(){cout<< UseResources()<< ENDL; }($)
$ / $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $

  Cat()
Dog()
〜Cat()
内部处理程序

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



那么, function try block

解决方案

UseResources 被定义如下:

  class UseResources 
{
class Cat *猫;
class狗狗;

public:
UseResources():cat(new Cat),dog(){cout<< UseResources()<< ENDL; }
〜UseResources(){delete cat; cat = NULL; cout< 〜UseResources()<< ENDL; }
};

如果 Dog :: Dog() throws ,那么 cat 会泄漏内存。 Becase UseResources 的构造函数永不完成,对象从未完全构建。因此它没有它的析构函数。



为了防止这种泄漏,你必须使用一个功能级别的try / catch块:

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

为了更完整地回答您的问题,功能级别的尝试/构造函数中的catch块专门做这种清理。功能级try / catch块不能吞下异常(常规可以)。如果他们抓到一些东西,他们会在到达catch块的末尾时再次抛出,除非你明确地用 throw 重新抛出。你可以将一种类型的异常转换为另一种类型的异常,但是你不能只是吞下它并且不会像没有发生。



这是为什么值和应该使用智能指针而不是裸指针,甚至是类成员。因为,像在你的情况下,如果你只有成员值而不是指针,你不必这样做。这是使用裸体指针(或其他形式的资源,无法在RAII对象中管理)来强制这种事情。



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






更多的原因不使用函数try块。上面的代码被细分破碎。考虑一下:

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

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



您可能会尝试通过使用复杂的lambda来纠正它只需新猫

  UseResources()try 
:cat({]() - > Cat * try {return new Cat;} catch(...){return nullptr;}}())
,dog()
{cout< < UseResources()<< ENDL; }
catch(...)
{
删除cat;
throw;
}

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



基本上,没有办法使此代码安全,除非新的Cat noexcept (明确地或隐含地)。



相比之下,这一直有效:

  class UseResources 
{
unique_ptr< Cat>猫;
狗狗;

public:
UseResources():cat(new Cat),dog(){cout<< UseResources()<< ENDL; }
〜UseResources(){cout<< 〜UseResources()<< ENDL; }
};

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


Possible Duplicate:
When is a function try block useful?
Difference between try-catch syntax for function

This code throws an int exception while constructing the Dog object inside class UseResources. The int exception is caught by a normal try-catch block and the code outputs :

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

Now, if we replace the definition of the UseResources() constructor, with one that uses a function try block, as below,

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

the output is the same

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

i.e., with exactly the same final result.

What is then, the purpose of a function try block ?

解决方案

Imagine if UseResources was defined like this:

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

If Dog::Dog() throws, then cat will leak memory. Becase UseResources's constructor never completed, the object was never fully constructed. And therefore it does not have its destructor called.

To prevent this leak, you must use a function-level try/catch block:

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

To answer your question more fully, the purpose of a function-level try/catch block in constructors is specifically to do this kind of cleanup. Function-level try/catch blocks cannot swallow exceptions (regular ones can). If they catch something, they will throw it again when they reach the end of the catch block, unless you rethrow it explicitly with throw. You can transform one type of exception into another, but you can't just swallow it and keep going like it didn't happen.

This is another reason why values and smart pointers should be used instead of naked pointers, even as class members. Because, as in your case, if you just have member values instead of pointers, you don't have to do this. It's the use of a naked pointer (or other form of resource not managed in a RAII object) that forces this kind of thing.

Note that this is pretty much the only legitimate use of function try/catch blocks.


More reasons not to use function try blocks. The above code is subtly broken. Consider this:

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

So, what happens in UseResources's constructor? Well, the expression new Cat will throw, obviously. But that means that cat never got initialized. Which means that delete cat will yield undefined behavior.

You might try to correct this by using a complex lambda instead of just new Cat:

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

That theoretically fixes the problem, but it breaks an assumed invariant of UseResources. Namely, that UseResources::cat will at all times be a valid pointer. If that is indeed an invariant of UseResources, then this code will fail because it permits the construction of UseResources in spite of the exception.

Basically, there is no way to make this code safe unless new Cat is noexcept (either explicitly or implicitly).

By contrast, this always works:

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

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

In short, look on a function-level try-block as a serious code smell.

这篇关于功能try块的目的是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆