C++11学习笔记7---资源回收RAII

参考文献:

(1)刘未鹏的那个经典的帖子:C++11(及现代C++风格)快速迭代式开发

http://mindhacks.cn/2012/08/27/modern-cpp-practices/

(2)在(1)的基础上另一个人写的帖子:异常安全,RAII与C++11

http://www.cnblogs.com/mavaL/articles/2515381.html

1.RAII---资源获取即初始化

(1)What

一种管理资源,避免资源泄露的方法。

其中资源包括:堆内存、开启的线程、打开的文件等等。

当然现在标准库已经确保很多东西是RAII的了,比如

①智能指针---管理内存

②标准IO---管理IO

③线程库---管理线程,锁什么的

但是:

①像socket这样的东西还没有标准库哦。

②想标准IO这样的饱受诟病库有时候不想用,想要自己实现哦。

③比如线程库,并不是所有都已经构建的很好,像mutex这样的会有lock_guard来自动释放,似乎线程并没有。

这个时候就需要自己编写代码来实现RAII啦。

(2)Why

资源管理的两大难点嘛:

①资源泄露

编码的时候,难免出错,忘记释放已分配的资源,这个时候资源就泄露了。

②异常安全

发生异常的时候,当前程序块会终止,异常代码后面的资源释放函数来不及执行。这个算一种特殊的资源泄露吧。

(3)How

①C++标准保证了任何情况下(不管程序块正常退出,还是异常退出),已构造的对象一定会被销毁,即它的析构函数最终会被调用。

②RAII的做法就是,使用一个对象,在其初始化时获取资源,在对象析构函数里释放资源


2.C++98对于RAII的实现难点

由于RAII要求用类的对象来管理资源。

而每种资源的释放方式又不同----归根结底,需要在对象的析构函数里调用对应资源的释放函数。

结果就是,每种资源,为了实现RAII,都要单独写一个资源的释放类。


3.C++11的解决办法

①只需要实现一个类,然后用std::function作为类对象构造函数的形参,接受不同的资源释放函数,将其放入析构函数里执行。

②实际调用时,使用Lambda作为实参,直接实现某种资源的释放操作,。

③就是RAII对象命名问题,可以采用宏定义的方式,自动获取行号为该对象命名,

然后,某次申请一个资源,就用宏构造一个释放对象,向其传入释放该资源的lambda函数即可。


这样,每次申请资源你就写一个释放对象,就不会存在忘记释放使得资源泄露的情况。

而且,遇到异常,也是能够将释放对象销毁,从而释放资源,是异常安全的。

两大问题就都解决了。


4.实际代码

以下的代码,来自刘未鹏那个帖子。

①理想情况下我们希望语言能够支持这样的范式:

void foo()
{
    HANDLE h = CreateFile(...);


    ON_SCOPE_EXIT { CloseHandle(h); }


    ... // use the file
}
 
<span style="font-size:18px;"><strong>②类的定义</strong></span>

class ScopeGuard
{
public:
    explicit ScopeGuard(std::function<void()> onExitScope)
        : onExitScope_(onExitScope), dismissed_(false)    //初始化两个成员变量

    { }


    ~ScopeGuard()
    {
        if(!dismissed_)
        {
            onExitScope_();
        }
    }


    void Dismiss()
    {
        dismissed_ = true;
    }


private:
    std::function<void()> onExitScope_;
    bool dismissed_;


private: // noncopyable
    ScopeGuard(ScopeGuard const&);
    ScopeGuard& operator=(ScopeGuard const&);
};

       
<span style="font-size:18px;"><strong>③Dismiss()的作用</strong></span>

Dismiss()函数也是Andrei的原始设计的一部分,其作用是为了支持rollback模式,

既然可以用RAII对象的析构函数进行释放对象,也可以用来进行其他功能,比如异常发生了进行回滚,回到一个之前的已知状态

例如:

ScopeGuard onFailureRollback([&] { /* rollback */ });
... // do something that could fail
onFailureRollback.Dismiss();

在上面的代码中,“do something”的过程中只要任何地方抛出了异常,rollback逻辑都会被执行。如果“do something”成功了,onFailureRollback.Dismiss()会被调用,设置dismissed_为true,阻止rollback逻辑的执行。


④资源回收对象命名问题

为了避免给这个对象起名的麻烦(如果有多个变量,起名就麻烦大了),可以定义一个宏,把行号混入变量名当中,

这样每次定义的ScopeGuard对象都是唯一命名的。

#define SCOPEGUARD_LINENAME_CAT(name, line) name##line
#define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)
#define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)

其中_LINE_会因为具体行不同,生成不同数字,这样就有不同的名字了,但是每次调用时都是一致的名字。


⑤最终形式

Acquire Resource1
ON_SCOPE_EXIT( [&] { /* Release Resource1 */ })
Acquire Resource2
ON_SCOPE_EXIT( [&] { /* Release Resource2 */ })
…


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值