外观
【02】RAII 资源获取即初始化
约 564 字大约 2 分钟
1. 前言
RAII 全称为 Resource Acquisition is Initialization,即 “资源获取即初始化”。所谓资源,包括
- 内存,传统做法可以通过
new
/delete
表达式来完成内存资源的申请和释放 - 文件句柄,传统做法可以通过
fopen
/fclose
C 库函数,又或者是open
/close
系统调用来完成文件的打开和关闭 - 网络连接,传统做法可以通过 Socket API 来完成网络连接的建立和断开,同样也是使用
open
/close
系统调用来完成
以及其他方面。资源获取即初始化则表示资源的获取能和对象的初始化绑定在一起,对象的初始化一般由构造函数完成,而资源的释放则由析构函数完成。因此使用 RAII 可以将资源的生命周期与对象的生命周期绑定在一起。这便是 RAII,现代 C++ 最重要也是最核心的概念之一。
2. 手动实现一个独占指针
C++ 动态存储期对象的创建一般需要使用 new
表达式,而该对象的释放需要手动使用 delete
,能不能把这个操作使用 RAII 进行管理?
当然可以。我们可以手动实现一个独占指针,我们考虑为普通的类对象设计独占指针。
#include <cstdio>
template <typename Tp>
class unique_ptr {
public:
unique_ptr(Tp *ptr) : _ptr(ptr) {}
~unique_ptr() { delete _ptr; }
unique_ptr(const unique_ptr&) = delete;
unique_ptr(unique_ptr&& other) noexcept : _ptr(other._ptr) {
other._ptr = nullptr;
}
unique_ptr& operator=(const unique_ptr&) = delete;
unique_ptr& operator=(unique_ptr&& other) noexcept {
_ptr = other._ptr;
other._ptr = nullptr;
return *this;
}
Tp& operator*() { return *_ptr; }
Tp* operator->() { return _ptr; }
private:
Tp *_ptr{};
};
template <typename Tp>
unique_ptr<Tp> make_unique() { return unique_ptr<Tp>(new Tp()); }
int main() {
auto ptr = make_unique<int>(42);
printf("%d\n", *ptr);
*ptr = 31;
printf("%d\n", *ptr);
return 0;
}
可以很清楚地看到,独占指针的实现充分利用了 RAII 的理念。在对象的构造过程中,资源的获取(即内存的分配)与对象的初始化绑定在了一起;而在对象的析构过程中,资源的释放(即内存的释放)也得到了自动管理。这种方式有效地避免了内存泄漏和悬 dangling 指针等问题,使得代码更加安全和易于维护。