使用RAII惯用法
RAII(Resource Acquisition Is Initialization, 资源获取即初始化)指资源在我们拿到的时候就已经初始化,一旦不需要该资源,就可以自动释放该资源。
对于C++来说,资源在构造函数中初始化(可以在构造函数中调用单独的初始化函数),在析构函数中释放或清理。常见的情形就是再函数调用过程中创建C++对象时分配资源,在C++对象出了作用域时将其自动清理和释放。
分配堆内存示例
如下代码,heapObj出了作用域该程序就会自动调用析构函数释放堆内存
// 定义HeapObjectWrapper RAII 对象
class HeapObjectWrapper
{
public:
HeapObjectWrapper(int size)
{
m_p = new char[size];
}
~HeapObjectWrapper()
{
delete[] m_p;
m_p = NULL;
}
private:
char* m_p;
};
// 使用 HeapObjectWrapper RAII对象
int main(void)
{
HeapObjectWrapper heapObj(1024);
if (测试操作) {}
return 0;
}
对多线程锁的获取和释放
在日常写的代码里,为了避免死锁,通常会在每个可能退出的分支上都释放锁,随着逻辑越来越复杂,可能会在某个可能退出的分支上忘记释放锁。
RAII可以解决这个问题:将锁包裹成一个对象,在构造函数中获取锁,在析构函数中释放锁。
伪代码示例:
class SomeLockGuard
{
public:
SomelockGuard()
{
// 加锁
m_lock.lock();
}
~SomeLockGuard()
{
// 解锁
m_lock.unlock();
}
private:
SomeLock m_lock;
};
int main(void)
{
SomeLockGuard lockWrapper;
if (条件1)
{
if (条件2) {
//操作1
return;
}
else {
// 操作2
return;
}
}
if (条件3)
{
// 操作3
return;
}
}
使用RAII惯用法之后,我们就再也不必在每个函数出口处都加上释放锁的代码了,因为在函数调用结束后会自动释放锁。
小结
资源泄漏和死锁等问题具有非常强的隐蔽性,如果在生产环境中出现这些问题,则难以复现,排查和定位问题。
只要理解并熟练使用RAII惯用法不仅能让我们的代码更加简洁和模块化,也能让我们在开发阶段避免一部分资源泄漏和死锁问题。