那里有很好的答案,所以我只是添加一些忘记的东西。
0。 RAII 是关于范围
RAII 是关于两者的:
- 在构造函数中获取资源(无论是什么资源),在析构函数中取消获取。
- 在声明变量时执行构造函数,并在变量超出范围时自动执行析构函数。
其他人已经回答过,所以我不会详细说明。
1。使用 Java 或 C# 编码时,您已经使用 RAII...
MONSIEUR JOURDAIN:什么!当我说,“妮可,把我的拖鞋拿来,
把我的睡帽给我,”这是散文吗?
哲学大师:是的,先生。
MONSIEUR JOURDAIN:四十多年来,我一直在说散文,但对此一无所知,我非常感谢你教会了我这一点。
——莫里哀:中产阶级绅士,第 2 幕,第 4 场
正如 Jourdain 先生对散文所做的那样,C# 甚至 Java 人已经在使用 RAII,但是以隐藏的方式。例如,以下 Java 代码(在 C# 中以相同的方式编写,将 synchronized 替换为 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 制作任何东西。例如,在 C++ 中没有直接内置等效于 synchronized 或 lock,但我们仍然可以拥有它们。
在 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 中用于在范围退出的情况下处理资源处置(通过return 或抛出的异常)。
精明的规范读者会注意到 C++ 没有 finally 子句。这不是错误,因为 C++ 不需要它,因为 RAII 已经处理了资源处理。 (相信我,编写 C++ 析构函数比编写正确的 Java finally 子句,甚至是 C# 的正确 Dispose 方法要容易得多。
不过,有时,finally 子句会很酷。我们可以在 C++ 中做到这一点吗? Yes, we can! 再次使用 RAII。
结论:RAII 不仅仅是 C++ 中的哲学:它是 C++
RAII?这是C++!!!
— C++ 开发者的愤怒评论,被无名的斯巴达国王和他的 300 位朋友无耻地抄袭
当您在 C++ 方面达到一定程度的经验时,您会开始考虑 RAII、构造函数和析构函数自动执行。
您开始考虑作用域,{ 和} 字符成为您代码中最重要的字符。
几乎所有东西都符合 RAII:异常安全、互斥体、数据库连接、数据库请求、服务器连接、时钟、操作系统句柄等,最后但并非最不重要的是内存。
数据库部分也不容忽视,因为如果你愿意付出代价,你甚至可以写成“事务性编程”的风格,执行一行又一行的代码,直到决定,在最后,如果您想提交所有更改,或者如果不可能,则将所有更改还原(只要每行至少满足强异常保证)。 (有关事务性编程,请参阅此 Herb's Sutter article 的第二部分)。
就像拼图一样,一切都适合。
RAII 是 C++ 的重要组成部分,没有它,C++ 就不可能是 C++。
这解释了为什么有经验的 C++ 开发人员如此迷恋 RAII,以及为什么 RAII 是他们在尝试另一种语言时首先搜索的内容。
它还解释了为什么垃圾收集器虽然本身就是一项了不起的技术,但从 C++ 开发人员的角度来看却没有那么令人印象深刻:
- RAII 已经处理了大部分由 GC 处理的案例
- GC 比 RAII 更好地处理纯托管对象的循环引用(通过智能使用弱指针来缓解)
- GC 仍然仅限于内存,而 RAII 可以处理任何类型的资源。
- 如上所述,RAII 可以做很多很多...