c++支持“finally”块吗?
RAII习语是什么?
c++的RAII习语和c#的using语句有什么区别?
c++支持“finally”块吗?
RAII习语是什么?
c++的RAII习语和c#的using语句有什么区别?
当前回答
正如许多人所说,解决方案是使用c++ 11的特性来避免最终阻塞。其中一个特性是unique_ptr。
以下是Mephane使用RAII模式编写的答案。
#include <vector>
#include <memory>
#include <list>
using namespace std;
class Foo
{
...
};
void DoStuff(vector<string> input)
{
list<unique_ptr<Foo> > myList;
for (int i = 0; i < input.size(); ++i)
{
myList.push_back(unique_ptr<Foo>(new Foo(input[i])));
}
DoSomeStuff(myList);
}
这里有更多关于在c++标准库容器中使用unique_ptr的介绍
其他回答
不,c++不支持'finally'块。原因是c++支持RAII:“资源获取是初始化”——对于一个真正有用的概念来说,这是一个糟糕的名字。
其思想是,对象的析构函数负责释放资源。当对象具有自动存储持续时间时,当创建对象的块退出时,对象的析构函数将被调用——即使该块在出现异常时退出。以下是Bjarne Stroustrup对这个话题的解释。
RAII的一个常见用途是锁定互斥量:
// A class with implements RAII
class lock
{
mutex &m_;
public:
lock(mutex &m)
: m_(m)
{
m.acquire();
}
~lock()
{
m_.release();
}
};
// A class which uses 'mutex' and 'lock' objects
class foo
{
mutex mutex_; // mutex for locking 'foo' object
public:
void bar()
{
lock scopeLock(mutex_); // lock object.
foobar(); // an operation which may throw an exception
// scopeLock will be destructed even if an exception
// occurs, which will release the mutex and allow
// other functions to lock the object and run.
}
};
RAII also simplifies using objects as members of other classes. When the owning class' is destructed, the resource managed by the RAII class gets released because the destructor for the RAII-managed class gets called as a result. This means that when you use RAII for all members in a class that manage resources, you can get away with using a very simple, maybe even the default, destructor for the owner class since it doesn't need to manually manage its member resource lifetimes. (Thanks to Mike B for pointing this out.)
For those familliar with C# or VB.NET, you may recognize that RAII is similar to .NET deterministic destruction using IDisposable and 'using' statements. Indeed, the two methods are very similar. The main difference is that RAII will deterministically release any type of resource -- including memory. When implementing IDisposable in .NET (even the .NET language C++/CLI), resources will be deterministically released except for memory. In .NET, memory is not deterministically released; memory is only released during garbage collection cycles.
†有些人认为“破坏是资源放弃”是RAII习语更准确的名称。
除了使基于堆栈的对象的清理变得容易之外,RAII还很有用,因为当对象是另一个类的成员时,同样的“自动”清理也会发生。当所属类被销毁时,由RAII类管理的资源将被清理,因为该类的dtor将被调用。
这意味着当您达到RAII的天堂,并且类中的所有成员都使用RAII(如智能指针)时,您可以为所有者类使用一个非常简单的(甚至是默认的)dtor,因为它不需要手动管理其成员资源生命周期。
RAII通常更好,但在c++中可以很容易地获得finally语义。使用少量的代码。
此外,c++核心指南最后给出了。
这里有一个到GSL微软实现的链接和一个到Martin Moene实现的链接
Bjarne Stroustrup多次表示,GSL中的所有内容最终都将被纳入标准。所以它最终应该是一种经得起考验的使用方式。
如果你想,你可以很容易地实现自己,继续阅读。
在c++ 11中RAII和lambdas允许做出一般的最后:
namespace detail { //adapt to your "private" namespace
template <typename F>
struct FinalAction {
FinalAction(F f) : clean_{f} {}
~FinalAction() { if(enabled_) clean_(); }
void disable() { enabled_ = false; };
private:
F clean_;
bool enabled_{true}; }; }
template <typename F>
detail::FinalAction<F> finally(F f) {
return detail::FinalAction<F>(f); }
使用示例:
#include <iostream>
int main() {
int* a = new int;
auto delete_a = finally([a] { delete a; std::cout << "leaving the block, deleting a!\n"; });
std::cout << "doing something ...\n"; }
输出将是:
doing something...
leaving the block, deleting a!
就我个人而言,我多次使用这个方法来确保在c++程序中关闭POSIX文件描述符。
有一个真正的类来管理资源,从而避免任何类型的泄漏通常是更好的,但这最终是有用的情况下,使一个类听起来有点多余。
此外,我喜欢它胜过其他语言,因为如果自然地使用它,你可以在开始代码附近编写结束代码(在我的例子中是new和delete),并且在c++中按照后进先出(LIFO)的顺序进行构造。唯一的缺点是你得到了一个你并不真正使用的auto变量,lambda语法使它有点嘈杂(在我的例子中,在第四行中,只有单词finally和右边的{}块是有意义的,其余的基本上都是嘈杂的)。
另一个例子:
[...]
auto precision = std::cout.precision();
auto set_precision_back = finally( [precision, &std::cout]() { std::cout << std::setprecision(precision); } );
std::cout << std::setprecision(3);
如果只有在失败的情况下才必须调用finally,则disable成员非常有用。例如,你必须在三个不同的容器中复制一个对象,你可以设置finally来撤销每次复制,并在所有复制成功后禁用。这样做,如果破坏不能扔,你就保证了强有力的保证。
禁用的例子:
//strong guarantee
void copy_to_all(BIGobj const& a) {
first_.push_back(a);
auto undo_first_push = finally([first_&] { first_.pop_back(); });
second_.push_back(a);
auto undo_second_push = finally([second_&] { second_.pop_back(); });
third_.push_back(a);
//no necessary, put just to make easier to add containers in the future
auto undo_third_push = finally([third_&] { third_.pop_back(); });
undo_first_push.disable();
undo_second_push.disable();
undo_third_push.disable(); }
如果你不能使用c++ 11,你仍然可以使用,但是代码会变得有点冗长。只需定义一个只有构造函数和析构函数的结构,构造函数引用所需的任何内容,而析构函数执行所需的操作。这就是lambda的作用,手动完成。
#include <iostream>
int main() {
int* a = new int;
struct Delete_a_t {
Delete_a_t(int* p) : p_(p) {}
~Delete_a_t() { delete p_; std::cout << "leaving the block, deleting a!\n"; }
int* p_;
} delete_a(a);
std::cout << "doing something ...\n"; }
希望你能使用c++ 11,这段代码更多地是为了表明“c++最终不支持”是如何从c++的最初几周开始就毫无意义的,甚至在c++得到它的名字之前就可以编写这种代码。
为什么即使是托管语言也会提供final块,尽管垃圾收集器会自动释放资源?
实际上,基于垃圾收集器的语言需要更多的“finally”。垃圾收集器不会及时销毁您的对象,因此不能依赖它正确地清理与内存无关的问题。
就动态分配数据而言,许多人认为应该使用智能指针。
然而……
RAII将异常安全的责任从对象的用户转移到设计人员
可悲的是,这是它自己的失败。旧的C编程习惯很难改掉。当您使用用C或非常C风格编写的库时,将不会使用RAII。除了重写整个API前端,这就是你必须要处理的。那么,“终于”这个词的缺失真的很伤人。
As pointed out in the other answers, C++ can support finally-like functionality. The implementation of this functionality that is probably closest to being part of the standard language is the one accompanying the C++ Core Guidelines, a set of best practices for using C++ edited by Bjarne Stoustrup and Herb Sutter. An implementation of finally is part of the Guidelines Support Library (GSL). Throughout the Guidelines, use of finally is recommended when dealing with old-style interfaces, and it also has a guideline of its own, titled Use a final_action object to express cleanup if no suitable resource handle is available.
因此,c++不仅最终支持,实际上还建议在许多常见用例中使用它。
GSL实现的示例使用如下所示:
#include <gsl/gsl_util.h>
void example()
{
int handle = get_some_resource();
auto handle_clean = gsl::finally([&handle] { clean_that_resource(handle); });
// Do a lot of stuff, return early and throw exceptions.
// clean_that_resource will always get called.
}
GSL的实现和使用与Paolo中的非常相似。Bolzoni的回答。一个区别是gsl::finally()创建的对象缺少disable()调用。如果您需要该功能(例如,在资源组装完成后返回资源,并且不会发生任何异常),那么您可能更喜欢Paolo的实现。否则,使用GSL就相当于使用标准化的特性。