在C, Cpp, Rust中的RAII设计模式
RAII是Resource Acquisition is Initialization的首字母缩写词。但,它的内容丰富,可不如首字母展开词汇那样意思直白。甚至,若直接去理解它的字面含义,你或许还会有一种不识其言的莫名其妙感。
【RAII设计模式】是将操作系统资源(物理概念)映射为代码内的对象(逻辑概念)。然后,自然而然地,
- 资源调度的生命周期 映射为 对象的生命周期
- 资源使用的申请环节 映射为 对象的构造函数 --- 对象生,则资源在。
- 资源释放的归还环节 映射为 对象的析构函数 --- 对象亡,则资源灭。
将对资源的分配/回收操作代码从散乱的【业务代码】里抽象出来,并隐藏于【变量-生命周期管理】的成熟架构之后。而变量管理的万般规则都服务于一个主旨:超出了作用域,就执行析构函数,给我释放掉。于是,就有
- 变量所属作用域终结 --- 如何终结不重要。管它是异常终止
throw,提前return,还是“寿终正寝”,这都不重要。 - 变量释放
- 系统资源回收 --- 只要资源随着变量一起被释放,那就绝对的“异常安全
exception safe”。
从code review的视角来审视,
- 业务代码也看着清爽 --- 少了随处的
alloc/new,与需要烧脑才能搞明白的dealloc/delete出现位置。 - 编译器也有更高级的事可做 --- 特别指
Rust。那繁杂的所有权规则,开发者都能给绕晕。
这岂不是【业务开发的码农】与【编译器大师】的双赢局面。和皆大欢喜美好结局。

C不支持RAII。若实现这个设计模式,开发者准备完全自己动手丰衣足食吧。或者寻找第三方库。Cpp从98版开始,标准库提供了对RAII的支持。但,需要调用std::scoped***(),std::make_unique(),std::make_shared()等专用构造器来意图明确地编码。同时,开发者还得随时警惕着遗留代码里的new和delete的非RAII代码(这类代码一般是非异常安全的not exception safe)。- 在
Rust里,对【内存/互斥锁/文件句柄】的分配与回收已经和Rust【所有权变量】的生命周期完全耦合在一起了。RAII被实现为语言规范的一部分,也是被强制要求的编码范式,开发者根本就没得选。包括对数据库连接的管理 --- 至少,从第三方库暴露出来的接口来看(源码没有研究过),其调用套路还是RAII风格的。
当Rust所有权变量超出了作用域时,
- 变量自身会被释放
- 该变量的析构函数也会自动释放被映射给它的资源
而这个过程是没有人工介入的。当然,你也可以借助std::mem::drop()函数来提早终结(实现了Drop Trait的)【所有权变量】的生命周期和释放系统资源。但,这对RAII在Rust里的全面落地没有影响,仅能算是对应用灵活性的有益补充。
1
共 3 条评论, 1 页
评论区
写评论学到了
涨姿势了
写的非常明白,手动点赞