C++ RAII 浅谈
RAII 这个名字
从我第一次听说 RAII 这个名词,到我大概知道 RAII 是什么,之间间隔了大概两三年。因为 RAII(Resource Acquisition Is Initialization 资源获取即初始化)这个名字实在是和 RAII 的内涵差的有点远。我一直试图对齐我对 RAII 的理解和对 RAII 这个名字的理解,导致我一直没懂 RAII 到底是啥。RAII 有个更好的名字叫 SBRM(Scope Bound Resource Management 受作用域束缚的资源管理)。由于 RAII 这个名字太烂了,所以只是 C++ 圈内的人才约定俗成的去用它,搜索引擎一搜 RAII,出来的全是 C++。
RAII 的含义
其实 RAII 的含义很简单,不过要理解其含义,首先要放弃理解它为什么叫 RAII,就把 RAII 当作一个约定的符号好了。
RAII 是一种管理资源的原则。这里资源是指从 OS 中分配的,有限的东西,比如内存、线程、文件描述符、锁等。管理这些资源是为了避免浪费,比如 new 了堆内存却忘记 delete,导致这块内存无法在程序中被重新利用,这叫内存泄漏。比如 open 了文件,却忘记 close、lock 了锁却忘记 unlock 等等。
RAII 是一种原则,按照 RAII 的原则编写代码能避免你忘记回收资源。由于在 C++ 中对象具有明确的生命周期,而且在对象的生命周期结束时编译器会自动插入析构对象的代码,而且程序员可以自定义析构函数,所以程序员可以将资源与对象绑定,在构造函数中分配资源,在析构函数中编写回收资源的代码,利用编译器自动在对象生命周期结束时调用析构函数的特性,自动回收资源。这种管理对象的原则或者思路就是 RAII。而且这里强调的是利用生命周期和析构函数自动回收资源。
引用 cppreference 的说法:具有 open/close,lock/unlock 成员函数的类就是典型的非 RAII设计。因为把回收资源的操作放在析构函数外,交给程序员自主自觉的去调用,没有解决容易往的问题。
RAII 的例子
提到“避免忘记 delete”,我们一定会想到智能指针,所以智能指针就是典型的符合 RAII 原则的东西。类似的还有 STL 容器,std::lock_guard<std::mutex> 等,在这些类型的对象退出作用域时,他们之前请求并拥有资源会自动释放。