C++11中的RAII
啥是RAII
按理说,您要是了解lambda的话,没有理由不知道RAII,但是小麦还是先提一下吧,为了本文的完整性嘛。RAII,Resource Acqueision Is Initialization,也就是资源获取即初始化。也就是说,在控制资源的声明周期的时候,在某个对象初始化(构造)的时候获取资源,而在对象销毁的时候(析构的时候释放资源。这一技术被广泛的使用在mutex中,典型的代码如下。
template<class LT>;
class guard_lock{
public:
guard_lock(LT &loc)
: m_lt(loc){m_lt.lock();}
~guard_lock(){m_lt.unlock();}
protected:
LT & m_lt;
};//end class guard_lock;
这样一来,这个就能够很给力的做到 即使释放锁资源,例如下面的代码:
guard_lock<Mutex> _l(mutex);
...
if(...)
return ;
if(another)
return ;
a_func_may_throw_exception();
通常,修改一下代码,然后,增加一个if条件,增加几个return是很正常的,如果不使用这样的RAII技术的话,就要在每个return的地方写上unlock()。RAII一下就解决了代码重复和维护性的问题。
问题
RAII看上去很强大,有什么问题么? 当然是有的,首先,每种资源的获取和释放方式都意味着不同的代码,这意味着对每种资源可能都需要特别的”guard_lock”,这。。。也很蛋疼,看一个例子也许能够更好的理解这种问题。实际中一个常见的问题是读一个文件,处理其中的数据,然后写文件。实际的编码中通常需要考虑读文件的路径不对的话,怎么办?写文件的路径不对呢?处理数据遇到异常了怎么办?
ifstream infile;
infile.open(infilename, ios::binary | ios::in);
if(!infile.is_open())
{
infile.close();
return -1;
}
ofstream outfile;
outfile.open(outfilename, ios::binary | ios::out);
if(!outfile.is_open())
{
outfile.close();
infile.close();
}
...
if(...) //other error happens
{
outfile.close();
infile.close();
}
这个例子中,文件的关闭操作被一再的调用和执行,这对于“相同代码只写一次”的强迫症来说,简直就是不能忍受的!
利用lambda
C++11引入了lambda,因此我们可以实现更加抽象的RAII类,在构造是执行一个函数,在析构时执行另外一个函数。
class scope_guard{
public:
template<class F1_t, class F2_t>
scope_guard(F1_t &&ctor, F2_t &&dtor)
: m_ctor(std::forward<F1_t>(ctor))
, m_dtor(std::forward<F2_t>(dtor)){
m_ctor();
}
~scope_guard(){m_dtor();}
protected:
std::function<void(void)> m_ctor;
std::function<void(void)> m_dtor;
};//end class scope_guard;
有了这个辅助类,上面的苦逼代码就能进化了!!!
ifstream infile;
scope_guard sg_infile([&infile, &infilename]() {infile.open(infilename, ios::binary | ios::in);},
[&infile]() {infile.close();});
ofstream outfile;
scope_guard sg_outfile([&outfile, &outfilename]() {outfile.open(outfilename, ios::binary | ios::out);},
[&outfile]() {outfile.close();});
if(!infile.is_open()) {
cout<<"cannot open file: "<< infilename<<endl;
return 1;
}
if(!outfile.is_open()) {
cout<<"cannot open file: "<<outfilename<<endl;
return 1;
}
有没有再也不用担心忘记错误处理的感觉!!!