C++之RAII技术解析

RAII(Resource Acquisition Is Initialization)是C++中的一种编程技术,通过将资源的生命周期绑定到对象的生命周期,实现资源自动管理和异常安全。本文详细介绍了RAII的概念、原理及实战应用,包括资源封装、作用域界定资源管理、标准库支持以及如何避免内存泄漏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       RAII 是 Resource Acquisition Is Initialization(资源获取即初始化)的缩写。RAII 是C++ 中常用的一种编程技术,具体是指必须在使用前获取的资源(如栈内存,线程,文件,锁,数据库连接,磁盘空间)的声明周期绑定在一个对象的生命周期,这样使用者就不需要自己去释放资源,避免资源泄露。 
RAII 保证资源可用于任何会访问该对象的函数。它亦保证所有资源的在其控制对象的生存期结束时被释放,以获取顺序的逆序。类似地,若资源获取失败(构造函数以异常退出),则所有已为完全构造的对象和基类子对象所获取的资源,会被以初始化顺序的逆序释放。这会有效利用语言特性(对象生存期、退出作用域、初始化顺序以及栈回溯)以消除内存泄漏并保证异常安全。此技术的另一名称是作用域界定的资源管理( Scope-Bound Resource Management , SBRM ),根据 RAII 对象的生存期在退出作用域时结束这一基本状况。 
RAII 可总结如下:

  • 将每个资源封装入一个类,这里 
    构造函数请求资源,并建立所有类不变量或在它无法完成时抛出异常, 
    析构函数释放资源并决不抛出异常;
  • 始终经由 RAII 类的实例使用满足要求的资源,该资源 
    自身拥有自动存储期或临时生存期,或 
    拥有绑定于自动或临时对象的生存期

拥有 open()/close() 、 lock()/unlock() ,或 init()/copyFrom()/destroy() 成员函数的类是非 RAII 类的典型的例子:

std::mutex m;

void bad() 
{
    m.lock();                    // 请求互斥
    f();                         // 若 f() 抛异常,则互斥决不被释放
    if(!everything_ok()) return; // 提早返回,互斥决不被释放
    m.unlock();                  // 若 bad() 抵达此语句,互斥才被释放
}

void good()
{
    std::lock_guard<std::mutex> lk(m); // RAII类:互斥请求即是初始化
    f();                               // 若f()抛异常,则释放互斥
    if(!everything_ok()) return;       // 提早返回,互斥被释放
}                                      // 若good()正常返回,则释放互斥

标准库 
     C++ 标准库遵循 RAII 管理其自身的资源: std::string 、 std::vector 、 std::thread ,以及多数其他类在构造函数(错误时抛出异常)中获取其资源,并在其析构函数(决不抛出)中释放之,且不要求显式清理。

另外,标准库提供几种 RAII包 装器以管理用户提供的资源:

  • std::unique_ptr 及 std::shared_ptr 以管理动态分配的内存,或以用户提供的删除器管理任何以普通指针表示的资源;
  • std::lock_guard 、 std::unique_lock 、 std::shared_lock 以管理互斥。

 

实战应用

2.1一个简单的例子:指针申请空间,释放空间

void Func()
{
  int *ip = new int[10];
  ...//operations
  ...//operations
  ...//operations
  delete[] ip;//if not free mem, memory overflow
}


使用RAII技术后:

template<class PointerType>
class My_Pointer
{
public:
   My_Pointer(PointerType* _ptr, size_t sz)
   {
        _ptr = new PointerType[sz];
        m_ptr = _ptr;
   }
   ~My_Pointer()
   {
        delete []m_ptr;
   }
protected:
   PointerType    m_ptr;
}


2.2 scope lock (局部锁技术)
在很多时候,为了实现多线程之间的数据同步,我们会使用到 mutex,critical section,event,singal 等技术。但在使用过程中,由于各种原因,有时候,我们会遇到一个问题:由于忘记释放(Unlock)锁,产生死锁现象。
采用RAII 就可以很好的解决这个问题,使用着不必担心释放锁的问题. 示例代码如下:
My_scope_lock 为实现 局部锁的模板类.
LockType 抽象代表具体的锁类 .如基于 mutex 实现 mutex_lock 类.

template<class LockType>
class My_scope_lock
{
   public:
   My_scope_lock(LockType& _lock):m_lock(_lock)
   { 
         m_lock.occupy();
   }
   ~My_scope_lock()
   {
        m_lock.relase();
   }
   protected:
   LockType    m_lock;
}


使用的时候:

//global vars
int counter = 0;
void routine();
mutex_lock  m_mutex_lock;
 
void routine()
{
    My_scope_lock l_lock(m_mutex_lock);
    counter++;
    ...//others...
}


我们可以根据上面的例子类推出好多这样例子。如读写文件的时候很容易忘记关闭文件,如果借用 RAII技术,就可以规避这种错误。再如对数据库的访问,忘记断开数据库连接等等都可以借助RAII 技术也解决。
 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值