< 返回我的博客

爱国的张浩予 发表于 2021-04-10 20:57

Tags:raii

C, Cpp, Rust中的RAII设计模式

RAIIResource Acquisition is Initialization的首字母缩写词。但,它的内容丰富,可不如首字母展开词汇那样意思直白。甚至,若直接去理解它的字面含义,你或许还会有一种不识其言的莫名其妙感。

RAII设计模式】是将操作系统资源(物理概念)映射为代码内的对象(逻辑概念)。然后,自然而然地,

  • 资源调度的生命周期 映射为 对象的生命周期
  • 资源使用的申请环节 映射为 对象的构造函数 --- 对象生,则资源在。
  • 资源释放的归还环节 映射为 对象的析构函数 --- 对象亡,则资源灭。

将对资源的分配/回收操作代码从散乱的【业务代码】里抽象出来,并隐藏于【变量-生命周期管理】的成熟架构之后。而变量管理的万般规则都服务于一个主旨:超出了作用域,就执行析构函数,给我释放掉。于是,就有

  • 变量所属作用域终结 --- 如何终结不重要。管它是异常终止throw,提前return,还是“寿终正寝”,这都不重要。
  • 变量释放
  • 系统资源回收 --- 只要资源随着变量一起被释放,那就绝对的“异常安全exception safe”。

code review的视角来审视,

  • 业务代码也看着清爽 --- 少了随处的alloc/new,与需要烧脑才能搞明白的dealloc/delete出现位置。
  • 编译器也有更高级的事可做 --- 特别指Rust。那繁杂的所有权规则,开发者都能给绕晕。

这岂不是【业务开发的码农】与【编译器大师】的双赢局面。和皆大欢喜美好结局。

rust-raii

  • C不支持RAII。若实现这个设计模式,开发者准备完全自己动手丰衣足食吧。或者寻找第三方库。
  • Cpp17版开始,标准库提供了对RAII的支持。但,需要调用std::scoped***(), std::make_unique(), std::make_shared()专用构造器来意图明确地编码。同时,开发者还得随时警惕着遗留代码里的newdelete的非RAII代码(这类代码一般是非异常安全的not exception safe)。
  • Rust里,对【内存/互斥锁/文件句柄】的分配与回收已经和Rust【所有权变量】的生命周期完全耦合在一起了。RAII被实现为语言规范的一部分,也是被强制要求的编码范式,开发者根本就没得选。包括对数据库连接的管理 --- 至少,从第三方库暴露出来的接口来看(源码没有研究过),其调用套路还是RAII风格的。

Rust所有权变量超出了作用域时,

  • 变量自身会被释放
  • 该变量的析构函数也会自动释放被映射给它的资源

而这个过程是没有人工介入的。当然,你也可以借助std::mem::drop()函数来提早终结(实现了Drop Trait的)【所有权变量】的生命周期和释放系统资源。但,这对RAIIRust里的全面落地没有影响,仅能算是对应用灵活性的有益补充。

更加生动的RAII的讲解,我推荐直接看这个视频。我也是在看了五遍这个视频之后,对原来的粗浅理解才有所提升。

评论区

写评论
w 2021-04-23 10:42

学到了

Mike Tang 2021-04-13 16:42

涨姿势了

kidd808 2021-04-10 21:21

写的非常明白,手动点赞

1 共 3 条评论, 1 页