C++ 资源获取即初始化(RAII)是什么意思?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2321511/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:01:28  来源:igfitidea点击:

What is meant by Resource Acquisition is Initialization (RAII)?

c++raii

提问by John

What is meant by Resource Acquisition is Initialization (RAII)?

资源获取即初始化(RAII)是什么意思?

回答by the_mandrill

It's a really terrible name for an incredibly powerful concept, and perhaps one of the number 1 things that C++ developers miss when they switch to other languages. There has been a bit of a movement to try to rename this concept as Scope-Bound Resource Management, though it doesn't seem to have caught on just yet.

对于一个非常强大的概念来说,这是一个非常糟糕的名字,也许是 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:

当我们说“资源”时,我们不仅仅指内存——它可以是文件句柄、网络套接字、数据库句柄、GDI 对象……简而言之,我们供应有限的东西,因此我们需要能够控制它们的使用。“范围绑定”方面意味着对象的生命周期绑定到变量的范围,因此当变量超出范围时,析构函数将释放资源。一个非常有用的特性是它提供了更大的异常安全性。例如,比较这个:

RawResourceHandle* handle=createNewResource();
handle->performInvalidOperation();  // Oops, throws exception
...
deleteResource(handle); // oh dear, never gets called so the resource leaks

With the RAII one

与 RAII 一

class ManagedResourceHandle {
public:
   ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {};
   ~ManagedResourceHandle() {delete rawHandle; }
   ... // omitted operator*, etc
private:
   RawResourceHandle* rawHandle;
};

ManagedResourceHandle handle(createNewResource());
handle->performInvalidOperation();

In this latter case, when the exception is thrown and the stack is unwound, the local variables are destroyed which ensures that our resource is cleaned up and doesn't leak.

在后一种情况下,当抛出异常并解开堆栈时,局部变量会被销毁,以确保我们的资源被清理并且不会泄漏。

回答by Péter T?r?k

This is a programming idiom which briefly means that you

这是一个编程习语,它的意​​思是你

  • encapsulate a resource into a class (whose constructor usually - but not necessarily** - acquires the resource, and its destructor always releases it)
  • use the resource via a local instance of the class*
  • the resource is automatically freed when the object gets out of scope
  • 将资源封装到一个类中(其构造函数通常 - 但不一定** - 获取资源,其析构函数总是释放它)
  • 通过类的本地实例使用资源*
  • 当对象超出范围时,资源会自动释放

This guarantees that whatever happens while the resource is in use, it will eventually get freed (whether due to normal return, destruction of the containing object, or an exception thrown).

这保证了资源在使用时无论发生什么,它最终都会被释放(无论是由于正常返回、包含对象的破坏还是抛出异常)。

It is a widely used good practice in C++, because apart from being a safe way to deal with resources, it also makes your code much cleaner as you don't need to mix error handling code with the main functionality.

这是 C++ 中广泛使用的良好实践,因为除了是处理资源的安全方式之外,它还使您的代码更加简洁,因为您不需要将错误处理代码与主要功能混合在一起。

*Update:"local" may mean a local variable, or a nonstatic member variable of a class. In the latter case the member variable is initialized and destroyed with its owner object.

*更新:“local”可能表示局部变量,或类的非静态成员变量。在后一种情况下,成员变量使用其所有者对象进行初始化和销毁​​。

**Update2:as @sbi pointed out, the resource - although often is allocated inside the constructor - may also be allocated outside and passed in as a parameter.

**Update2:正如@sbi 所指出的,资源 - 尽管通常在构造函数内部分配 - 也可以在外部分配并作为参数传入。

回答by sbi

"RAII" stands for "Resource Acquisition is Initialization" and is actually quite a misnomer, since it isn't resource acquisition(and the initialization of an object) it is concerned with, but releasingthe resource (by means of destructionof an object).
But RAII is the name we got and it sticks.

“RAII”代表“资源获取即初始化”,实际上用词不当,因为它所关注的不是资源获取(和对象的初始化),而是释放资源(通过销毁对象的方式) )。
但 RAII 是我们得到的名字,它坚持下去。

At its very heart, the idiom features encapsulating resources (chunks of memory, open files, unlocked mutexes, you-name-it) in local, automatic objects, and having the destructor of that object releasing the resource when the object is destroyed at the end of the scope it belongs to:

从本质上讲,该习语的特点是将资源(内存块、打开的文件、未锁定的互斥体、您命名的)封装在本地自动对象中,并在对象被销毁时让该对象的析构函数释放资源。它所属的范围结束:

{
  raii obj(acquire_resource());
  // ...
} // obj's dtor will call release_resource()

Of course, objects aren't always local, automatic objects. They could be members of a class, too:

当然,对象并不总是本地的、自动的对象。他们也可以是一个类的成员:

class something {
private:
  raii obj_;  // will live and die with instances of the class
  // ... 
};

If such objects manage memory, they are often called "smart pointers".

如果这样的对象管理内存,它们通常被称为“智能指针”。

There are many variations of this. For example, in the first code snippets the question arises what would happen if someone wanted to copy obj. The easiest way out would be to simply disallow copying. std::unique_ptr<>, a smart pointer to be part of the standard library as featured by the next C++ standard, does this.
Another such smart pointer, std::shared_ptrfeatures "shared ownership" of the resource (a dynamically allocated object) it holds. That is, it can freely be copied and all copies refer to the same object. The smart pointer keeps track of how many copies refer to the same object and will delete it when the last one is being destroyed.
A third variant is featured by std::auto_ptrwhich implements a kind of move-semantics: An object is owned by only one pointer, and attempting to copy an object will result (through syntax hackery) in transferring ownership of the object to the target of the copy operation.

这有很多变化。例如,在第一个代码片段中,问题出现了如果有人想要复制obj. 最简单的方法是简单地禁止复制。std::unique_ptr<>,作为下一个 C++ 标准特色的标准库的一部分的智能指针,执行此操作。
另一个这样的智能指针,std::shared_ptr具有它所拥有的资源(一个动态分配的对象)的“共享所有权”。也就是说,它可以自由复制,并且所有副本都指向同一个对象。智能指针会跟踪有多少副本引用了同一个对象,并在最后一个被销毁时将其删除。
第三个变体的特点是std::auto_ptr它实现了一种移动语义:一个对象只由一个指针拥有,并且试图复制一个对象将导致(通过语法黑客)将对象的所有权转移到复制操作的目标。

回答by elmiomar

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 newis used to create such an object. And to destroy the object, the operator deletecan be used. Objects created by the operator neware dynamically allocated, i.e. allocated in dynamic memory (also called heapor free store). So, an object that was created by newwill continue to exist until it's explicitly destroyed using delete.

对象的生命周期由其作用域决定。然而,有时我们需要创建一个独立于创建它的作用域的对象,或者它很有用。在 C++ 中,运算符new用于创建这样的对象。并且要销毁对象,delete可以使用操作符。操作符创建的对象new是动态分配的,即在动态内存(也称为空闲存储)中分配。因此,由 创建的对象new将继续存在,直到使用delete.

Some mistakes that can occur when using newand deleteare:

使用new和时可能出现的一些错误delete是:

  • Leaked object(or memory): using newto allocate an object and forget to deletethe object.
  • Premature delete(or dangling reference): holding another pointer to an object, deletethe object, and then use the other pointer.
  • Double delete: trying to deletean object twice.
  • 泄漏的对象(或内存):new用于分配对象并忘记delete该对象。
  • 过早删除(或悬空引用):持有另一个指向对象的指针,delete该对象,然后使用另一个指针。
  • 双重删除:尝试delete两次删除一个对象。

Generally, scoped variables are preferred. However, RAII can be used as an alternative to newand deleteto 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 objectends, without the need for explicit cleanup.

通常,首选作用域变量。然而,RAII可以用作替代newdelete使对象活独立地是对其范围的。这种技术包括获取指向在堆上分配的对象的指针并将其放置在句柄/管理器对象中。后者有一个析构函数,负责销毁对象。这将保证该对象可供任何想要访问它的函数使用,并且在句柄对象的生命周期结束时销毁该对象,而无需显式清除。

Examples from the C++ standard library that use RAII are std::stringand std::vector.

来自使用 RAII 的 C++ 标准库的示例是std::stringstd::vector

Consider this piece of code:

考虑这段代码:

void fn(const std::string& str)
{
    std::vector<char> vec;
    for (auto c : str)
        vec.push_back(c);
    // do something
}

when you create a vector and you push elements to it, you don't care about allocating and deallocating such elements. The vector uses newto allocate space for its elements on the heap, and deleteto free that space. You as a user of vector you don't care about the implementation details and will trust vector not to leak. In this case, the vector is the handle objectof its elements.

当您创建一个向量并将元素推送到它时,您不关心分配和取消分配这些元素。该向量new用于为其在堆上的元素分配空间,并delete释放该空间。作为 vector 的用户,您并不关心实现细节,并且会相信 vector 不会泄漏。在这种情况下,向量是其元素的句柄对象

Other examples from the standard library that use RAII are std::shared_ptr, std::unique_ptr, and std::lock_guard.

标准库中的其他例子是使用RAII是std::shared_ptrstd::unique_ptrstd::lock_guard

Another name for this technique is SBRM, short for Scope-Bound Resource Management.

此技术的另一个名称是SBRM,是Scope-Bound Resource Management 的缩写。

回答by Dennis

The book C++ Programming with Design Patterns Revealeddescribes RAII as:

C++ Programming with Design Patterns Revealed》一书将 RAII 描述为:

  1. Acquiring all resources
  2. Using resources
  3. Releasing resources
  1. 获取所有资源
  2. 使用资源
  3. 释放资源

Where

在哪里

  • Resources are implemented as classes, and all pointers have class wrappers around them (making them smart pointers).

  • Resources are acquired by invoking their constructors and released implicitly (in reverse order of acquiring) by invoking their destructors.

  • 资源被实现为类,所有的指针都有类包装器(使它们成为智能指针)。

  • 资源通过调用它们的构造函数来获取,并通过调用它们的析构函数隐式释放(以与获取相反的顺序)。

回答by Mohammad Moridi

There are three parts to an RAII class:

RAII 类分为三个部分:

  1. The resource is relinquished in the destructor
  2. Instances of the class are stack allocated
  3. The resource is acquired in the constructor. This part is optional, but common.
  1. 资源在析构函数中被放弃
  2. 类的实例是堆栈分配的
  3. 在构造函数中获取资源。这部分是可选的,但很常见。

RAII stands for "Resource Acquisition is initialization." The "resource acquisition" part of RAII is where you begin something that must be ended later, such as:

RAII 代表“资源获取即初始化”。RAII 的“资源获取”部分是您开始必须稍后结束的事情的地方,例如:

  1. Opening a file
  2. Allocating some memory
  3. Acquiring a lock
  1. 打开文件
  2. 分配一些内存
  3. 获取锁

The "is initialization" part means that the acquisition happens inside the constructor of a class.

“是初始化”部分意味着获取发生在类的构造函数内部。

https://www.tomdalling.com/blog/software-design/resource-acquisition-is-initialisation-raii-explained/

https://www.tomdalling.com/blog/software-design/resource-acquisition-is-initialisation-raii-explained/

回答by Dmitry Pavlov

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 方式中,Toptal 工程师 Peter Goodspeed-Niklaus 向我们展示了垃圾收集器的历史,并解释了所有权和借用的概念如何帮助消除垃圾收集器而不影响其安全保证。