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

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

问题描述


可能重复:

什么时候函数try块有用?

函数的try-catch语法之间的区别


此代码在构建 Dog 对象的过程中引发 int 异常 UseResources int 异常由正常的 try-catch 块捕获,代码输出:

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

  #include< iostream> 
using namespace 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; } catch(int){} 

输出是相同的

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



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



function try block c>

解决方案 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() ,那么 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只是新的Cat

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

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



基本上,没有办法使此代码安全,除非 new 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天全站免登陆