功能try块的目的是什么? [英] What is the purpose of a function try block?
问题描述
可能重复:
什么时候函数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
会泄漏内存。 BecaseUseResources
的构造函数永不完成,对象从未完全构建。因此它没有它的析构函数。
为了防止这种泄漏,你必须使用一个功能级别的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 functionThis code throws an
int
exception while constructing theDog
object inside classUseResources
. Theint
exception is caught by a normaltry-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 afunction 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, thencat
will leak memory. BecaseUseResources
'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 expressionnew Cat
will throw, obviously. But that means thatcat
never got initialized. Which means thatdelete 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, thatUseResources::cat
will at all times be a valid pointer. If that is indeed an invariant ofUseResources
, then this code will fail because it permits the construction ofUseResources
in spite of the exception.Basically, there is no way to make this code safe unless
new Cat
isnoexcept
(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屋!