什么是资源获取初始化(RAII)?
当前回答
对于一个非常强大的概念来说,这是一个非常糟糕的名字,而且可能是c++开发人员在转向其他语言时最容易忽略的事情之一。有一些人试图将这个概念重命名为作用域绑定资源管理(Scope-Bound Resource Management),尽管它似乎还没有流行起来。
When we say 'Resource' we don't just mean memory - it could be file handles, network sockets, database handles, GDI objects... In short, things that we have a finite supply of and so we need to be able to control their usage. The 'Scope-bound' aspect means that the lifetime of the object is bound to the scope of a variable, so when the variable goes out of scope then the destructor will release the resource. A very useful property of this is that it makes for greater exception-safety. For instance, compare this:
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
用RAII这个
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
在后一种情况下,当抛出异常并展开堆栈时,局部变量将被销毁,这确保了我们的资源被清理并且不会泄漏。
其他回答
许多人认为RAII是用词不当,但实际上它是这个习语的正确名称,只是没有很好地解释它。
Wikipedia explained behavior in detail: Resource acquisition is initialization (RAII) is a programming idiom used in several object-oriented, statically-typed programming languages to describe a particular language behavior. In RAII, holding a resource is a class invariant, and is tied to object lifetime: resource allocation (or acquisition) is done during object creation (specifically initialization), by the constructor, while resource deallocation (release) is done during object destruction (specifically finalization), by the destructor. In other words, resource acquisition must succeed for initialization to succeed. Thus the resource is guaranteed to be held between when initialization finishes and finalization starts (holding the resources is a class invariant), and to be held only when the object is alive. Thus if there are no object leaks, there are no resource leaks.
至于名称,它仅仅意味着“资源获取”操作是初始化操作,应该是资源类对象的初始化/构造函数的一部分。换句话说,使用这种习惯用法,使用资源意味着创建一个资源类来保存资源,并在构造类对象时初始化资源。它隐式地建议资源的释放应该在资源类析构函数中对称地发生。
这有什么用呢? 当然,您可以选择不使用这个成语,但是如果您想知道使用这个成语会得到什么,请考虑一下
RAII 对于更大的c++项目来说,在构造函数/析构函数对之外不包含对new或delete(或malloc/free)的单个调用是很常见的。或者实际上根本没有。
你可以避免
Exit: free_resource() //在退出函数前清除资源
或者使用RAII锁,这样你就不会忘记解锁。
Manual memory management is a nightmare that programmers have been inventing ways to avoid since the invention of the compiler. Programming languages with garbage collectors make life easier, but at the cost of performance. In this article - Eliminating the Garbage Collector: The RAII Way, Toptal engineer Peter Goodspeed-Niklaus gives us a peek into the history of garbage collectors and explains how notions of ownership and borrowing can help eliminate garbage collectors without compromising their safety guarantees.
我已经多次回到这个问题并阅读了它,我认为投票最多的答案有点误导。
RAII的关键是:
“这(主要)不是关于捕捉异常,主要是关于管理资源的所有权。”
得票最高的答案夸大了例外安全,这让我很困惑。
事实是:
You still need to write try catch to handle exceptions (check the 2 code example below), except that you don't need to worry about releasing resources for those classes using RAII in your catch block. Otherwise, you need to look up each non-RAII class's API to find which function to call so as to release acquired resources in catch block. RAII simply save these work. Similar as above, when coding with RAII, you simply write less code, no need to call releasing resouce functions. All the clean-ups are done in the destructor.
另外,请查看我在上面的评论中发现的两个有用的代码示例。
https://ideone.com/1Jjzuc, https://ideone.com/xm2GR9
附言:我们可以用..来和蟒蛇比较。As语句,你也需要捕获可能发生在with块内部的异常。
对于一个非常强大的概念来说,这是一个非常糟糕的名字,而且可能是c++开发人员在转向其他语言时最容易忽略的事情之一。有一些人试图将这个概念重命名为作用域绑定资源管理(Scope-Bound Resource Management),尽管它似乎还没有流行起来。
When we say 'Resource' we don't just mean memory - it could be file handles, network sockets, database handles, GDI objects... In short, things that we have a finite supply of and so we need to be able to control their usage. The 'Scope-bound' aspect means that the lifetime of the object is bound to the scope of a variable, so when the variable goes out of scope then the destructor will release the resource. A very useful property of this is that it makes for greater exception-safety. For instance, compare this:
RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation(); // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks
用RAII这个
class ManagedResourceHandle {
public:
ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
~ManagedResourceHandle() {delete rawHandle; }
... // omitted operator*, etc
private:
RawResourceHandle* rawHandle;
};
ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();
在后一种情况下,当抛出异常并展开堆栈时,局部变量将被销毁,这确保了我们的资源被清理并且不会泄漏。
An object's lifetime is determined by its scope. However, sometimes we need, or it is useful, to create an object that lives independently of the scope where it was created. In C++, the operator new is used to create such an object. And to destroy the object, the operator delete can be used. Objects created by the operator new are dynamically allocated, i.e. allocated in dynamic memory (also called heap or free store). So, an object that was created by new will continue to exist until it's explicitly destroyed using delete.
使用new和delete时会出现以下错误:
泄漏对象(或内存):使用new分配对象,忘记删除对象。 过早删除(或悬空引用):持有指向对象的另一个指针,删除对象,然后使用另一个指针。 重复删除:尝试删除一个对象两次。
Generally, scoped variables are preferred. However, RAII can be used as an alternative to new and delete to make an object live independently of its scope. Such a technique consists of taking the pointer to the object that was allocated on the heap and placing it in a handle/manager object. The latter has a destructor that will take care of destroying the object. This will guarantee that the object is available to any function that wants access to it, and that the object is destroyed when the lifetime of the handle object ends, without the need for explicit cleanup.
c++标准库中使用RAII的例子有std::string和std::vector。
考虑下面这段代码:
void fn(const std::string& str)
{
std::vector<char> vec;
for (auto c : str)
vec.push_back(c);
// do something
}
当你创建一个向量并将元素推入它时,你不关心这些元素的分配和释放。vector使用new为其堆上的元素分配空间,并使用delete释放该空间。作为vector的用户,您不关心实现细节,并且相信vector不会泄漏。在本例中,vector是其元素的句柄对象。
其他使用RAII的标准库示例有std::shared_ptr、std::unique_ptr和std::lock_guard。
这种技术的另一个名称是SBRM,是作用域绑定资源管理(Scope-Bound Resource Management)的缩写。
推荐文章
- 为什么我的程序不能在Windows 7下用法语编译?
- 如何获取变量的类型?
- 什么是奇怪的重复模板模式(CRTP)?
- 连接两个向量的最佳方法是什么?
- 在c++中,是通过值传递更好,还是通过引用到const传递更好?
- 在STL中deque到底是什么?
- Windows上最好的免费c++分析器是什么?
- 如何自动转换强类型枚举为int?
- 在一个类中使用具有成员函数的泛型std::function对象
- 'for'循环中的后增量和前增量产生相同的输出
- 虚函数和纯虚函数的区别
- c++中的_tmain()和main()有什么区别?
- 内存泄漏是否正常?
- 当启用c++ 11时,std::vector性能回归
- 什么时候使用哪种指针?