c++支持“finally”块吗?
RAII习语是什么?
c++的RAII习语和c#的using语句有什么区别?
c++支持“finally”块吗?
RAII习语是什么?
c++的RAII习语和c#的using语句有什么区别?
当前回答
为什么即使是托管语言也会提供final块,尽管垃圾收集器会自动释放资源?
实际上,基于垃圾收集器的语言需要更多的“finally”。垃圾收集器不会及时销毁您的对象,因此不能依赖它正确地清理与内存无关的问题。
就动态分配数据而言,许多人认为应该使用智能指针。
然而……
RAII将异常安全的责任从对象的用户转移到设计人员
可悲的是,这是它自己的失败。旧的C编程习惯很难改掉。当您使用用C或非常C风格编写的库时,将不会使用RAII。除了重写整个API前端,这就是你必须要处理的。那么,“终于”这个词的缺失真的很伤人。
其他回答
另一个“finally”块模拟使用c++ 11 lambda函数
template <typename TCode, typename TFinallyCode>
inline void with_finally(const TCode &code, const TFinallyCode &finally_code)
{
try
{
code();
}
catch (...)
{
try
{
finally_code();
}
catch (...) // Maybe stupid check that finally_code mustn't throw.
{
std::terminate();
}
throw;
}
finally_code();
}
让我们希望编译器会优化上面的代码。
现在我们可以这样写代码:
with_finally(
[&]()
{
try
{
// Doing some stuff that may throw an exception
}
catch (const exception1 &)
{
// Handling first class of exceptions
}
catch (const exception2 &)
{
// Handling another class of exceptions
}
// Some classes of exceptions can be still unhandled
},
[&]() // finally
{
// This code will be executed in all three cases:
// 1) exception was not thrown at all
// 2) exception was handled by one of the "catch" blocks above
// 3) exception was not handled by any of the "catch" block above
}
);
如果你愿意,你可以把这个习语包装成“try - finally”宏:
// Please never throw exception below. It is needed to avoid a compilation error
// in the case when we use "begin_try ... finally" without any "catch" block.
class never_thrown_exception {};
#define begin_try with_finally([&](){ try
#define finally catch(never_thrown_exception){throw;} },[&]()
#define end_try ) // sorry for "pascalish" style :(
现在"finally"块在c++ 11中可用:
begin_try
{
// A code that may throw
}
catch (const some_exception &)
{
// Handling some exceptions
}
finally
{
// A code that is always executed
}
end_try; // Sorry again for this ugly thing
就我个人而言,我不喜欢“宏”版本的“finally”习语,宁愿使用纯粹的“with_finally”函数,即使在这种情况下语法更笨重。
您可以在这里测试上面的代码:http://coliru.stacked-crooked.com/a/1d88f64cb27b3813
PS
如果你的代码中需要一个finally块,那么作用域守卫或ON_FINALLY/ON_EXCEPTION宏可能会更好地满足你的需求。
下面是ON_FINALLY/ON_EXCEPTION的用法示例:
void function(std::vector<const char*> &vector)
{
int *arr1 = (int*)malloc(800*sizeof(int));
if (!arr1) { throw "cannot malloc arr1"; }
ON_FINALLY({ free(arr1); });
int *arr2 = (int*)malloc(900*sizeof(int));
if (!arr2) { throw "cannot malloc arr2"; }
ON_FINALLY({ free(arr2); });
vector.push_back("good");
ON_EXCEPTION({ vector.pop_back(); });
...
总之,微软Visual c++确实支持try,它在MFC应用中一直被用作捕获严重异常的方法,否则会导致崩溃。例如;
int CMyApp::Run()
{
__try
{
int i = CWinApp::Run();
m_Exitok = MAGIC_EXIT_NO;
return i;
}
__finally
{
if (m_Exitok != MAGIC_EXIT_NO)
FaultHandler();
}
}
在过去,我用它来做一些事情,比如在退出之前保存打开文件的备份。不过,某些JIT调试设置会破坏这种机制。
I also think that RIIA is not a fully useful replacement for exception handling and having a finally. BTW, I also think RIIA is a bad name all around. I call these types of classes 'janitors' and use them a LOT. 95% of the time they are neither initializing nor acquiring resources, they are applying some change on a scoped basis, or taking something already set up and making sure it's destroyed. This being the official pattern name obsessed internet I get abused for even suggesting my name might be better.
我只是认为不合理的做法是,要求某些特殊列表的每一个复杂设置都必须编写一个类来包含它,以避免在清理过程中出现问题时需要捕获多个异常类型时的复杂性。这将导致大量的临时类,否则这些类是不必要的。
是的,对于专为管理特定资源而设计的类,或者专为处理一组类似资源而设计的泛型类,这是没问题的。但是,即使所有涉及的东西都有这样的包装器,清理的协调也可能不仅仅是简单的反序析构函数调用。
我认为c++有一个final。我的意思是,天哪,在过去的几十年里,它被粘上了这么多零碎的东西,似乎奇怪的是,人们突然对一些东西变得保守起来,比如最终,它可能非常有用,可能不像其他一些已经添加的东西那么复杂(尽管这只是我的猜测)。
不一定,但你可以在一定程度上模仿他们,例如:
int * array = new int[10000000];
try {
// Some code that can throw exceptions
// ...
throw std::exception();
// ...
} catch (...) {
// The finally-block (if an exception is thrown)
delete[] array;
// re-throw the exception.
throw;
}
// The finally-block (if no exception was thrown)
delete[] array;
注意,final -block本身可能在原始异常被重新抛出之前抛出一个异常,从而丢弃原始异常。这与Java final -block中的行为完全相同。同样,你不能在try&catch块中使用return。
除了使基于堆栈的对象的清理变得容易之外,RAII还很有用,因为当对象是另一个类的成员时,同样的“自动”清理也会发生。当所属类被销毁时,由RAII类管理的资源将被清理,因为该类的dtor将被调用。
这意味着当您达到RAII的天堂,并且类中的所有成员都使用RAII(如智能指针)时,您可以为所有者类使用一个非常简单的(甚至是默认的)dtor,因为它不需要手动管理其成员资源生命周期。