基础介绍
RAII (Resource Acquisition Is Initialization) 是一种 C++ 编程范式,这不是一个语法特性,而是一种处理方式。RAII的思想:
- 资源获取与对象初始化同时发生
- 资源释放与对象销毁同时发生
- 通过对象的生命周期来管理资源,确保资源的安全使用
怎么理解RAII呢?前面说了这不是一种库特性,更像是一种约定,一种管理资源的方式,c++标准库某些库特性提供了RAII资源给管理的特性,用户自己设计的类型也可以按照RAII的思想进行设计。
RAII特点
自动资源管理
这是一个核心的特点,核心就是资源管理,要确保资源的获取和销毁与对象的生命周期一致。通过这个特点可以实现资源的自动清理,防止资源泄露。请看下面的例子:
class FileHandler
{private:FILE* file;public: //构造函数获取资源FileHandler(char* filename){ file = fopen(filename, "r");if(!file) throw std::runtime_error("failed to open file");}//析构函数释放资源~FileHandler(){if(file)fclose(file);}};
异常安全
void processFile() {FileHandler fh("data.txt"); // 获取资源// 如果这里抛出异常,FileHandler的析构函数仍会被调用// 确保文件被正确关闭doSomething();
} // 作用域结束,自动调用析构函数释放资源
常见的RAII应用场景
智能指针
智能指针std::unique_ptr<T>在实现中就采用了RAII的变成范式,当创建这个指针被构造函数构造时,就会获取资源,当std::unique_ptr<T>变量声明周期结束时,就会自动释放该指针对应的对象。注意std::shared_ptr<T>变量不时RAII的思想,这种类型的变量是需要根据引用计数的数量来决定资源是否释放。示例如下所示:
class Resource {
public:void doWork() { /* ... */ }
};void foo() {std::unique_ptr<Resource> ptr(new Resource()); // RAII管理动态内存ptr->doWork();// 不需要手动删除,unique_ptr析构时会自动删除
}
互斥锁的管理
class Lock
{private:std::mutex& mtx;public:Lock(std::mutex& m):mtx(m){mtx.lock(); //构造时加锁}~Lock(){mtx.unlock(); //析构时解锁}
};void funtion()
{std::mutex mtx;Lock lock(mtx); //此处加锁.....
}//函数结束自定解锁
数据库连接
class DBConnection {
private:Connection* conn;
public:DBConnection(const std::string& connectionString) {conn = DatabaseConnect(connectionString);if (!conn) throw std::runtime_error("Connection failed");}~DBConnection() {if (conn) {DatabaseDisconnect(conn);}}
};
优秀实践
不要使用裸指针
这里不使用裸指针的意思是,如果使用裸指针就需要自己管理这个裸指针的释放,如有可能尽可能使用一些智能指针,比如std::unique_ptr<T>,也可以使用std::shared_ptr<T>。请看下面的 例子:
Resource* source = new Resource();
....... //业务逻辑
delete source; //手动释放资源std::unique_ptr<Resource> res = std::make_unique<Resource>();//生命周期结束自动结束
使用标准的RAII工具
- 智能指针:std::unique_ptr std::shared_ptr std::weak_ptr
- 互斥锁和线程同步:std::lock_guard<T> std::unique_lock std::scoped_lock std::shared_lock
- 标准容器:所有的标准容器都是RAII的,如set map vecotor等