Answers:
资源获取即初始化意味着对象应将自己作为一个完整的程序包照顾自己,而不希望其他代码告诉实例“嘿,您很快就会被清理-请立即整理。” 通常确实意味着析构函数中有一些有意义的东西。这也意味着您要编写一个专门用于管理资源的类,知道在某些难以预测的情况下(例如引发异常),您可以依靠析构函数执行。
假设您要编写一些代码,将Windows光标更改为等待光标(沙漏,无法工作的甜甜圈等),进行处理,然后再将其改回。并且还说“尽力而为”可能会引发异常。RAII的方法是创建一个类,其ctor将光标设置为等待状态,该类的一个“真实”方法完成了您想要执行的操作,并且其dtor将光标重新设置了。资源(在这种情况下为游标状态)与对象的范围相关。获取资源时,将初始化一个对象。如果抛出异常,您可以依靠被破坏的对象,这意味着您可以依靠清理资源。
很好地使用RAII意味着您不需要finally
。当然,它依赖于确定性破坏,这在Java中是无法做到的。您可以使用C#和VB.NET获得某种确定性的销毁using
。
RAII的部分内容是确定对象何时对其自己的清理负责–规则是对象是否应在其构造函数初始化完成时以及何时完成。初始化和清除,构造函数和析构函数的对称性意味着两者之间有着紧密的联系。
RAII的要点之一是确保异常安全-引发异常时应用程序保持自洽。乍一看,这是微不足道的-当异常导致作用域退出时,该作用域中的局部变量需要被销毁。
但是,如果异常抛出发生在构造函数中会发生什么呢?
好吧,该对象尚未完全构建,因此无法安全销毁。构造函数应根据需要具有try块,以确保在传播异常之前进行所有必要的清除。一旦异常在构造对象的范围之外传播,就不会进行析构函数调用,因为该对象不是首先构造的。
特别要考虑要销毁的对象内部的成员数据的构造函数。如果其中之一抛出异常,则您的主要构造函数代码将根本不会运行-但是构成该构造函数隐式部分的某些代码将具有该异常。成功构建的所有成员都将自动销毁。没有构造的任何成员(包括引发异常的成员)都不是。
因此,从根本上说,RAII是一项政策,可确保所有已完全构建的对象都将被及时销毁,尤其是在出现异常引发的情况下,并且确保任何对象要么已完全构建,要么没有(完全没有-您不知道如何安全清理的构造对象)。分配的资源也被释放。而且很多工作都是自动化的,因此程序员不必为此太担心。