RAII

RAII(Resource Acquisition Is Initialization),即资源获取时就是初始化,指资源在被我们拿到的时候就已经初始化好了,一旦不再需要该资源,就可以自动释放。

通过RAII 来管理内存

在C++中,通常是在类的构造函数中进行初始化,在类的析构里面释放资源。

一个简单的示例:

template<typename T>
class RaiiWrapper {
public:
    RaiiWrapper() = delete;
    explicit RaiiWrapper(T *data){
        m_data = data;
    }
    ~RaiiWrapper(){
            delete m_data;
    }
private:
    T *m_data;
};

在实例化RaiiWrapper时,传入指针,在析构时delete。

有了这种机制。可以将分配在堆内存上面的对象的生命周期,和一个分配在栈上的对象的生命周期相绑定,

这样当栈对象出了作用域被析构的时候,堆上的对象将一块被析构。

通过RAII机制 来关闭句柄或者解锁。

在一些情况下,当我们初始化了一个句柄,但是在后面的逻辑中出现了错误,这个时候我们需要关闭当前打开的句柄。

//伪代码
void function() {
    handle = initHandle();
    if (cond == false) {
        handle.close();
        return;
    }
    if (cond1 == false) {
        handle.close();
        return;
    }
    if (cond2 == false) {
        handle.close();
        return;
    }
}

这种情况下,当出现错误的时候,我们直接想要函数返回,这样就会导致在每个判断中都需要去调用handle.close()

class HandleWrapper {
public:
    HandleWrapper() {
        m_handle = initHandle();
    }
    ~HandleWrapper() {
        m_handle.close();
    }
private:
    Handle m_handle;
};

//这样我们不用去关系什么时候调用handle.close();
//因为函数返回时,handle 会调用析构函数,然后执行调用close() 
void function() {
    HandleWrapper handle;
	if (cond2 == false) { return; }
	if (cond2 == false) { return; }
    if (cond2 == false) { return; }
}

又例如,在某些情况下,我们得到了锁,为了避免产生死锁,我们需要在操作完成或者发生错误的时候解锁。

//伪代码
void function() {
    //得到锁
    glocker.lock();
    if (cond == false) {
        glocker.unlock();
        return;
    }
    if (cond1 == false) {
        glocker.unlock();
        return;
    }
    if (cond2 == false) {
        glocker.unlock();
        return;
    }
    glocker.unlock();
    return;
}

使用RAII机制改写后:

//伪代码
class Locker {
public:
    Locker() {
        m_locker.lock();
    }
    ~Locker() {
        m_locker.unlcok();
    }
private:
    mutex m_locker;
};

//这样也不用担心在出错时,忘记解锁。
//在函数返回时,会自动调用unlock()
void function() {
    //得到锁
	Locker locker();
	if (cond1 == false) { return; }
	if (cond2 == false) { return; }
    if (cond3 == false) { return; }

    return;
}