什么是RAII?例子?


19

总是使用RAII一词时,人们实际上是在谈论解构而不是初始化。我想我对这可能意味着什么有基本的了解,但我不太确定。另外:C ++是唯一的RAII语言吗?Java或C#/。NET呢?

Answers:


25

资源获取即初始化意味着对象应将自己作为一个完整的程序包照顾自己,而不希望其他代码告诉实例“嘿,您很快就会被清理-请立即整理。” 通常确实意味着析构函数中有一些有意义的东西。这也意味着您要编写一个专门用于管理资源的类,知道在某些难以预测的情况下(例如引发异常),您可以依靠析构函数执行。

假设您要编写一些代码,将Windows光标更改为等待光标(沙漏,无法工作的甜甜圈等),进行处理,然后再将其改回。并且还说“尽力而为”可能会引发异常。RAII的方法是创建一个类,其ctor将光标设置为等待状态,该类的一个“真实”方法完成了您想要执行的操作,并且其dtor将光标重新设置了。资源(在这种情况下为游标状态)与对象的范围相关。获取资源时,将初始化一个对象。如果抛出异常,您可以依靠被破坏的对象,这意味着您可以依靠清理资源。

很好地使用RAII意味着您不需要finally。当然,它依赖于确定性破坏,这在Java中是无法做到的。您可以使用C#和VB.NET获得某种确定性的销毁using


4
我认为这就是您要解决的问题,但是您可能想补充一点,为什么Java和C#不支持RAII的原因是垃圾收集器。在C ++中,本地对象一旦超出范围就会被销毁。在Java / C#中不是这样。
杰森·贝克

在Jasons的观点上进行扩展,之所以Java和C#不能保证及时销毁是因为存在引用循环的可能性,这意味着无法确定运行析构函数的安全顺序。参考循环也可以在C ++中发生,但含义有所不同-程序员负责确定销毁顺序并进行显式删除。这种责任通常打包在某些更高级别的析构函数中-例如,容器类负责确保所有包含的项都被析构。“所有权”是关键。
Steve314 2010年

1
@Jason就是我所说的“确定性破坏”的意思-C ++程序员知道析构函数何时运行。
凯特·格雷戈里

我知道这是一个旧答案,但我仍然有些困惑。我刚刚了解了这个词,一些信息说,获取应该在构造函数中进行。这对我来说真的没有意义,这个答案似乎与此矛盾,但是您能否澄清?
Per Johansson

1
@PerJohansson是的,您获得了ctor。然后您在dtor中释放。我当时专注于第二点,但是他们在一起。一旦完成ctor,您就知道您有一个有效的对象。而且您知道无论发生什么情况,资源都会在正确的时间释放。
凯特·格雷戈里

4

RAII的部分内容是确定对象何时对其自己的清理负责–规则是对象是否应在其构造函数初始化完成时以及何时完成。初始化和清除,构造函数和析构函数的对称性意味着两者之间有着紧密的联系。

RAII的要点之一是确保异常安全-引发异常时应用程序保持自洽。乍一看,这是微不足道的-当异常导致作用域退出时,该作用域中的局部变量需要被销毁。

但是,如果异常抛出发生在构造函数中会发生什么呢?

好吧,该对象尚未完全构建,因此无法安全销毁。构造函数应根据需要具有try块,以确保在传播异常之前进行所有必要的清除。一旦异常在构造对象的范围之外传播,就不会进行析构函数调用,因为该对象不是首先构造的。

特别要考虑要销毁的对象内部的成员数据的构造函数。如果其中之一抛出异常,则您的主要构造函数代码将根本不会运行-但是构成该构造函数隐式部分的某些代码将具有该异常。成功构建的所有成员都将自动销毁。没有构造的任何成员(包括引发异常的成员)都不是。

因此,从根本上说,RAII是一项政策,可确保所有已完全构建的对象都将被及时销毁,尤其是在出现异常引发的情况下,并且确保任何对象要么已完全构建,要么没有(完全没有-您不知道如何安全清理的构造对象)。分配的资源也被释放。而且很多工作都是自动化的,因此程序员不必为此太担心。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.