资源获取是什么意思初始化(RAII)?
资源获取是什么意思初始化(RAII)?
Answers:
这是一个令人难以置信的强大概念的名字,真是一个可怕的名字,也许是C ++开发人员在切换到其他语言时错过的第一件事。试图将这个概念重命名为“ 范围绑定资源管理”已经有一些动作,尽管它似乎尚未流行。
当我们说“资源”时,我们不仅指内存,还可以是文件句柄,网络套接字,数据库句柄,GDI对象...总之,我们有有限的供应,因此我们需要能够控制其用法。“范围绑定”方面意味着对象的生存期绑定到变量的范围,因此,当变量超出范围时,析构函数将释放资源。它的一个非常有用的属性是它可以提高异常安全性。例如,比较一下:
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();
在后一种情况下,当引发异常并取消堆栈堆栈时,局部变量将被销毁,以确保我们的资源被清理并且不会泄漏。
Scope-Bound
由于存储类说明符与范围一起确定了实体的存储持续时间,因此不太确定此处是否是最佳名称选择。将其范围缩小到范围限制可能是一个有用的简化方法,但是它不是100%精确的
这是一个编程习语,简要表示您
这样可以保证无论使用资源时发生什么,它最终都会被释放(无论是由于正常返回,包含对象的破坏还是引发的异常)。
这是C ++中广泛使用的良好实践,因为除了安全地处理资源之外,它还使您的代码更加简洁,因为您无需将错误处理代码与主要功能混在一起。
*
更新: “ local”可能意味着局部变量或类的非静态成员变量。在后一种情况下,成员变量将使用其所有者对象初始化和销毁。
**
Update2:正如@sbi指出的那样,资源-尽管通常是在构造函数内部分配的,但也可以在外部分配并作为参数传递。
open()
/ close()
方法来初始化和释放资源,只有构造函数和析构函数,因此资源的“持有”只是对象的生存期,无论该生存期是否为由上下文(堆栈)或显式(动态分配)处理
“ RAII”代表“资源获取是初始化”,实际上用词不当,因为它不是它所关注的资源获取(和对象的初始化),而是释放资源(通过销毁对象) )。
但是RAII是我们获得的名称,并且坚持下去。
从本质上讲,该惯用语具有将资源(内存块,打开的文件,解锁的互斥锁,you-name-it)封装在本地自动对象中的功能,并在对象被销毁时销毁该对象的析构函数以释放资源。所属范围的末端:
{
raii obj(acquire_resource());
// ...
} // obj's dtor will call release_resource()
当然,对象并不总是本地的自动对象。他们也可以成为班级成员:
class something {
private:
raii obj_; // will live and die with instances of the class
// ...
};
如果此类对象管理内存,则通常将它们称为“智能指针”。
这有很多变化。例如,在第一个代码段中,出现了一个问题,如果有人要复制,该怎么办obj
。最简单的方法是简单地禁止复制。std::unique_ptr<>
,它是下一个C ++标准所具有的,成为标准库一部分的智能指针。
另一个此类智能指针的std::shared_ptr
特征是它拥有的资源(动态分配的对象)的“共享所有权”。也就是说,可以自由复制它,并且所有副本都引用同一个对象。智能指针跟踪引用同一对象的副本数,并在销毁最后一个副本时将其删除。
第三种变体的特点是std::auto_ptr
它实现了一种移动语义:一个对象仅由一个指针拥有,并且尝试复制一个对象(通过语法黑客)将导致将该对象的所有权转移到复制操作的目标。
std::auto_ptr
是的过时版本std::unique_ptr
。std::auto_ptr
C ++ 98中尽可能多的一种模拟移动语义,std::unique_ptr
使用了C ++ 11的新移动语义。之所以创建新类,是因为C ++ 11的move语义更加明确(需要std::move
从临时目录中删除),而默认情况下,它用于来自非const的任何副本std::auto_ptr
。
对象的生存期由其范围决定。但是,有时我们需要创建一个对象,该对象与创建对象的作用域无关,这是有用的,或者很有用。在C ++中,运算符new
用于创建这样的对象。并销毁对象,delete
可以使用运算符。由操作员创建的对象new
是动态分配的,即在动态内存(也称为堆或空闲存储)中分配。因此,由创建的对象new
将继续存在,直到使用明确销毁为止delete
。
使用new
和时可能发生的一些错误delete
是:
new
用于分配对象并忘记delete
该对象。delete
,然后再使用另一个指针。delete
两次对象。通常,首选范围变量。然而,RAII可以用作替代new
和delete
使对象活独立地是对其范围的。这种技术包括将指针分配到在堆上分配的对象,并将其放置在handle / manager对象中。后者具有一个析构函数,将负责销毁对象。这将确保该对象可用于任何想要访问它的函数,并且该对象在句柄对象的生存期结束时将被销毁,而无需显式清理。
来自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
}
当创建向量并将元素推入向量时,您不必担心分配和取消分配此类元素。向量用于new
在堆上为其元素分配空间,并delete
释放该空间。作为vector的用户,您无需关心实现细节,并且会相信vector不会泄漏。在这种情况下,向量是其元素的句柄对象。
标准库中的其他例子是使用RAII是std::shared_ptr
,std::unique_ptr
和std::lock_guard
。
该技术的另一个名称是SBRM,是范围绑定资源管理的缩写。
揭示了具有设计模式的C ++编程这本书将RAII描述为:
哪里
资源被实现为类,所有指针周围都有类包装器(使它们成为智能指针)。
通过调用其构造函数来获取资源,并通过调用其析构函数来隐式释放资源(以相反的顺序)。
RAII类分为三个部分:
RAII代表“资源获取是初始化”。RAII的“资源获取”部分是您开始一些必须在以后结束的事情的地方,例如:
“初始化”部分意味着获取发生在类的构造函数内部。
自从编译器发明以来,手动内存管理是程序员一直在想办法避免的噩梦。使用垃圾回收器进行编程的语言可以使生活更轻松,但是会降低性能。在本文“ 消除垃圾收集器:RAII方式”中,Toptal工程师Peter Goodspeed-Niklaus向我们介绍了垃圾收集器的历史,并解释了所有权和借用的概念如何在不损害其安全保证的前提下消除垃圾收集器。