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;
}