C++ 里的 RAII 是什么
2024-02-23
写 C++ 的人经常会遇到一个词,RAII(Resource Acquisition Is Initialization)。初看令人疑惑。
展开看,中文翻译让人更困惑了:资源获取即初始化。
什么是资源?什么叫初始化?这两者又存在什么关系?
今天这篇文章,就是想从三个方面简单谈谈,什么是 RAII,怎么使用,以及对于我的启发。
1. 一个通俗的例子
空谈 RAII 也许不容易理解。
有个图书馆。图书馆里有很多书。图书馆要借书给人看。
如果借的书没有及时归还,图书馆里的书就会越来越少。
因此图书馆有个规定:借书的时候要登记,给定时间内要办理归还手续。
于是图书馆便借助一借一还两个流程,实现了书籍的管理。
2. 举个代码的例子
从一个简单的例子开始。
这里把std::mutex
当作是一份资源。资源的使用通过lock/unlock
实现。
但这里并没有显式使用这两个函数。相反,借助std::lock_guard
一个辅助类间接管理了std::mutex
。
给不熟悉 C++ 的读者简单解释下。主要分为两点。
- C++ 提供给开发者多个关于对象生命周期的,用前端的行话说,Hook 函数。其中最主要的就是构造函数和析构函数,分别在对象构造和析构的时候执行
- C++ 中栈上对象的生命周期在一对
{}
内,因此构造函数会在进入括号内执行,析构函数执行于离开括号时
如此一来,std::lock_guard
的极简实现就很清楚了:在类构造的时候执行lock
,在析构的时候unlock
即可。
好处也很明显。
一方面不需要显式地加锁/释放锁(自动),另一方面因为构造/析构函数一定会结伴执行,因此不会存在加锁后忘记释放的情况,降低开发者的心智负担(安全)。
这里使用到的技巧,就叫做:RAII。
通俗来说,按照个人理解,就是将资源的管理放在类的构造和析构函数中,如此,类的生命周期就是资源的使用周期。
RAII 使得资源管理自动化,自动化则意味着更低的犯错可能。对于开发者而言,只要考虑清楚构造和析构函数中应该做的事,就可以解放双手,拥抱美好生活了。
这里的资源,不仅限于内存、网络连接、文件句柄等,任何你需要精确掌控的对象,都可以借助 RAII 管理。
比如,在 C 语言中经常出现的内存泄漏问题,在 C++11 中,通过智能指针(smart pointer)一劳永逸地解决了。其核心思想也是 RAII:在类构造的时候申请堆上内存,在析构的时候释放。
忘记 free、double free、野指针等问题便被很好的规避了。
由此可见,RAII 是个利器。
3. 一些启示
人是很容易犯错的。
关于这点,墨菲定律很早就诠释了。有人把它理解为某种“诅咒”,不过我倒觉得这未尝不是一种启示:既然墨菲定律不可避免,我们就应该尽早想办法规避。
就像内存管理。既然手动维护容易出错,那我们就让这个管理自发进行。
自动化是个很好的方法。因为它的流程是确定的,过程是可复现的,人的因素被大大降低。因此在平时,如果有办法,我们便应该将任务委托,委托给计算机,委托给机器,委托给靠谱的人。当然,自己也要努力变得靠谱,被别人很好地使用。
另一点,便是有始有终。或者用高级点的词描述:闭环。
用我之前在百度时经常听到的一句话概括,便是:
凡事有交代,件件有着落,事事有回音。
(完)
参考
- 本文作者:Plantree
- 本文链接:https://plantree.me/blog/2024/raii/
- 版权声明:所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
最后更新于: 2024-11-20T09:44:17+08:00