在 C++ 的发展历程中,有一个极为重要的核心理念:RAII(Resource Acquisition Is Initialization,资源获取即初始化)。通过这个理念,能够使得开发者编写出更加安全、健壮的程序。
1. 问题场景
在 C++ 程序中,我们经常需要使用和管理各种资源,例如:动态分配的内存、文件句柄、网络连接、数据库连接、互斥锁等,这些资源往往需要手动初始化和释放,一旦程序发生异常、提前返回,或逻辑较为复杂,就很容易导致资源泄漏或死锁等问题。
#include <iostream>
#include <stdexcept>
#include <mutex>
void demo01()
{
int* my_data = new int[100]; // 动态申请内存
std::cout << "内存已分配" << std::endl;
// 假设:在某些情况下,此处抛出异常
throw std::runtime_error("发生异常!");
// 下面代码永远不会被执行
delete[] my_data;
std::cout << "内存已释放" << std::endl;
}
std::mutex my_mutex;
void demo02()
{
my_mutex.lock();
std::cout << "已加锁" << std::endl;
// 假设:在某些情况下,你需要提前返回
return;
// 下面代码永远不会被执行
my_mutex.unlock();
std::cout << "锁已释放" << std::endl;
}
int main() {
try
{
//demo01();
demo02();
}
catch (const std::exception& e)
{
std::cout << "捕获异常:" << e.what() << std::endl;
}
return 0;
}
2. 解决思路
要想让资源管理既安全又简洁,一个非常有效的方法是:
把资源的初始化和释放(生命周期)的管理绑定到一个对象 obj 上
- 对象 obj 构造函数执行时,自动获取资源
- 对象 obj 析构函数执行时,自动释放资源
这样,我们无需手动释放资源,即使程序异常退出或提前返回,也能确保资源得到正确回收。这就是 RAII 的核心思想。
#include <iostream>
#include <stdexcept>
#include <mutex>
class RAIIMemory {
int* data_;
public:
explicit RAIIMemory(int size) : data_(new int[size])
{
std::cout << "内存已分配" << std::endl;
}
~RAIIMemory()
{
delete[] data_;
std::cout << "内存已释放" << std::endl;
}
int* get() { return data_; }
};
void demo01()
{
// 将动态内存资源交给 my_data 对象管理
// my_data 创建时,获得资源
// my_data 析构时,释放资源
RAIIMemory my_data(10);
// 即使异常发生,由于栈回退机制,也会正确释放资源
throw std::runtime_error("发生异常!");
}
class RAIIMutex
{
std::mutex& mtx_;
public:
explicit RAIIMutex(std::mutex& m) : mtx_(m)
{
mtx_.lock();
std::cout << "已加锁" << std::endl;
}
~RAIIMutex()
{
mtx_.unlock();
std::cout << "锁已释放" << std::endl;
}
};
std::mutex my_mutex;
void demo02()
{
RAIIMutex lock(my_mutex);
// 即使提前返回,锁资源也能够及时释放
return;
}
int main() {
try
{
//demo01();
demo02();
}
catch (const std::exception& e)
{
std::cout << "捕获异常:" << e.what() << std::endl;
}
return 0;
}
当然,在实际开发中,我们无需自己封装锁管理类,C++ 标准库已经提供了符合 RAII 思想的类型,例如:
std::unique_ptr:独占资源所有权,析构时自动释放内存std::shared_ptr:多个指针共享资源所有权,引用计数为 0 时自动释放std::ifstream/std::ofstream:文件流对象,析构时自动关闭文件std::lock_guard:最常用的作用域锁,构造时加锁,析构时自动解锁
RAII 通过将资源的生命周期与对象的生命周期绑定,实现了资源的自动管理,并保障异常安全。


冀公网安备13050302001966号