那里有很好的答案,所以我只添加一些被遗忘的东西。
0. RAII与范围有关
RAII涉及以下两个方面:
- 在构造函数中获取资源(无论什么资源),然后在析构函数中取消获取它。
- 让构造函数在声明变量时执行,而析构函数在变量超出范围时自动执行。
其他人已经对此做出了回答,因此我不再赘述。
1.使用Java或C#进行编码时,您已经在使用RAII ...
MONSIEUR JOURDAIN:什么!当我说“妮可,给我带来拖鞋,给我睡帽”时,是散文吗?
哲学大师:是的,先生。
MONSIEUR JOURDAIN:四十多年来,我一直在不了解散文的情况下讲散文,而且我很乐意告诉您这一点。
—莫里哀:中产阶级绅士,第二幕,第四幕
正如Jourdain先生对散文所做的那样,C#甚至Java人士已经在使用RAII,但是它们以隐藏的方式使用。例如,以下Java代码(用替换synchronized
为C#,以相同的方式编写lock
):
void foo()
{
// etc.
synchronized(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
...已经在使用RAII:互斥锁获取是通过关键字(synchronized
或lock
)完成的,并且在退出示波器时将完成未获取。
它的符号很自然,即使对于从未听说过RAII的人也几乎不需要解释。
C ++比Java和C#的优势在于,可以使用RAII进行任何操作。例如,有没有直接内建等效的synchronized
,也没有lock
在C ++中,但我们仍然可以拥有它们。
在C ++中,它将被编写为:
void foo()
{
// etc.
{
Lock lock(someObject) ; // lock is an object of type Lock whose
// constructor acquires a mutex on
// someObject and whose destructor will
// un-acquire it
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
可以很容易地用Java / C#方式编写(使用C ++宏):
void foo()
{
// etc.
LOCK(someObject)
{
// if something throws here, the lock on someObject will
// be unlocked
}
// etc.
}
2. RAII有其他用途
白兔子:[唱歌]我迟到了/我迟到了/约会很重要。/没时间说“你好”。/ 再见。/我迟到了,我迟到了,我迟到了。
—爱丽丝梦游仙境(迪士尼版,1951年)
您知道何时将调用构造函数(在对象声明处),并且何时将调用其对应的析构函数(在范围的出口处),因此只需一行就可以编写几乎神奇的代码。欢迎来到C ++仙境(至少从C ++开发人员的角度来看)。
例如,您可以编写一个计数器对象(作为练习),仅通过声明其变量即可使用它,就像上面的锁对象一样:
void foo()
{
double timeElapsed = 0 ;
{
Counter counter(timeElapsed) ;
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
当然,可以使用宏将Java / C#方式编写为哪种:
void foo()
{
double timeElapsed = 0 ;
COUNTER(timeElapsed)
{
// do something lengthy
}
// now, the timeElapsed variable contain the time elapsed
// from the Counter's declaration till the scope exit
}
3.为什么C ++缺乏finally
?
[大喊]这是最后的倒计时!
—欧洲:最后的倒计时(对不起,我在这里没有报价,... :-)
该finally
子句在C#/ Java中用于在范围退出(通过a return
或引发的异常)的情况下处理资源处置。
精通规范的读者会注意到C ++没有finally子句。这不是错误,因为C ++不需要它,因为RAII已经处理了资源处置。(并且相信我,编写C ++析构函数要比编写正确的Java finally子句甚至C#正确的Dispose方法容易得多)。
有时候,一个finally
子句仍然很酷。我们可以用C ++做到吗?我们可以!再次使用RAII。
结论:RAII不仅仅是C ++中的哲学:它是C ++
RAII?这是C ++ !!!
— C ++开发人员的愤怒评论,由晦涩难懂的斯巴达国王和他的300个朋友无耻复制
当你到达的用C的经验一定程度++,你在以下方面开始思考RAII,在以下方面自动执行construtors和析构函数。
您开始考虑范围,并且{
和}
字符成为代码中最重要的字符。
几乎所有的东西都适合RAII:异常安全性,互斥锁,数据库连接,数据库请求,服务器连接,时钟,操作系统句柄等,以及最后但并非最不重要的内存。
数据库部分不可忽略,因为如果您接受支付,甚至可以以“ 事务性编程 ”风格编写代码,执行几行代码,直到最终确定是否要提交所有更改为止,或者,如果不可能的话,将所有更改恢复原状(只要每行至少满足“强异常保证”)。(请参阅此Herb Sutter文章的第二部分有关事务性编程,)。
就像一个难题,一切都适合。
RAII是C ++的重要组成部分,没有它,C ++就不可能成为C ++。
这就解释了为什么经验丰富的C ++开发人员对RAII如此着迷,以及为什么RAII是他们尝试另一种语言时首先要搜索的东西。
它解释了为什么垃圾收集器虽然本身是一项宏伟的技术,但从C ++开发人员的角度来看却没有那么令人印象深刻:
- RAII已经处理了GC处理的大多数案件
- 与纯托管对象上的循环引用相比,GC的处理要比RAII更好(通过巧妙使用弱指针来缓解)
- GC仍然限于内存,而RAII可以处理任何类型的资源。
- 如上所述,RAII可以做的更多,更多...