使用C11特性,实现对象自动释放(RAll)的方式(ScopeGuard )

参考:https://www.cnblogs.com/chenny7/p/11990105.html

RAII的本质内容是用对象代表资源,把管理资源的任务转化为管理对象的任务,将资源的获取和释放与对象的构造和析构对应起来,从而确保在对象的生存期内资源始终有效,对象销毁时资源必被释放。换句话说,拥有对象就等于拥有资源,对象存在则资源必定存在。由此可见,RAII惯用法是进行资源管理的有力武器。

 

 

ScopeGuard

 ScopeGuard 最大的用处也是释放资源。

比如分配内存,做某些操作,再释放内存,很多人会这样写:

void* data = malloc(size);
xxxxx
if (xxx) {
    xxxxx
}
else {
    xxxxx
}

free(data);

这样的代码是很脆弱的,有下面问题。

  • malloc 和 free 可能会相隔很远,难以看出它们的对应关系。
  • 另外中间有任何异常,中途 return,free 都不能被执行,就有资源泄露。
  • 假如将这段代码复用,搬到另外地方,容易漏掉 free。

为解决资源释放问题,有些老旧的 C/C++ 代码,会采用折衷写法,会使用 do {} while(false),甚至会用到 goto。 比如:

do {
    if (xxx) {
        break;
    }

    if (xxx) {
        break;
    }

    xxx 正常操作
    return 1;
} while(false);

free(data);
xxxx 释放资源
return 0;

或者

if (xxxx) {
        goto fail;
    }

    if (xxxx) {
        goto fail;
    }

    xxx 正常操作
    return 1;

fail:
    free(data);
    xxxx 释放资源
    return 0;

对比 ScopeGuard 的写法:

void* data = malloc(size);
ON_SCOPE_EXIT {
    free(data);
};

xxxxx
if (xxx) {
    xxxxx
}
else {
    xxxxx
}

资源一旦分配,接下来就立即使用 ScopeGuard 释放资源,这样分配和释放就会靠在一起,当退出作用域的的时候,里面的语句就被执行,释放掉资源。

无论下面的语句抛异常也好,中途退出也好,代码都是安全的。这样的代码也更容易修改,比如将其移动到另外的地方,它还是安全的。假如分配和释放分隔两地,移动代码时就很容易漏掉某些语句。

在Golang语言中,有defer关键字可以实现上面例子中的scope guard,而C++需要自己实现。

 

C++ 实现用到 C++ 11 的 lamda,定义对象将 lamda 存起来,在析构函数中调用。这个 lamda 在不同的场合也有不同的叫法,比如匿名函数,闭包,代码块,block。

有些小地方需要注意:一个是存储 lamda 使用了模板,而不是 std::function, 这个可以避免 lamda 转 std::function 的开销(尽管这个开销在绝大多数情况下可以忽略不计)。

#ifndef __SCOPE_GUARD_H__
#define __SCOPE_GUARD_H__

#define __SCOPEGUARD_CONCATENATE_IMPL(s1, s2) s1##s2
#define __SCOPEGUARD_CONCATENATE(s1, s2) __SCOPEGUARD_CONCATENATE_IMPL(s1, s2)

#if defined(__cplusplus)

#include <type_traits>

// ScopeGuard for C++11
namespace clover {
    template <typename Fun>
    class ScopeGuard {
    public:
        ScopeGuard(Fun &&f) : _fun(std::forward<Fun>(f)), _active(true) {
        }

        ~ScopeGuard() {
            if (_active) {
                _fun();
            }
        }

        void dismiss() {
            _active = false;
        }

        ScopeGuard() = delete;
        ScopeGuard(const ScopeGuard &) = delete;
        ScopeGuard &operator=(const ScopeGuard &) = delete;

        ScopeGuard(ScopeGuard &&rhs) : _fun(std::move(rhs._fun)), _active(rhs._active) {
            rhs.dismiss();
        }

    private:
        Fun _fun;
        bool _active;
    };

    namespace detail {
        enum class ScopeGuardOnExit {};

        template <typename Fun>
        inline ScopeGuard<Fun> operator+(ScopeGuardOnExit, Fun &&fn) {
            return ScopeGuard<Fun>(std::forward<Fun>(fn));
        }
    } // namespace detail
} // namespace clover

// Helper macro
#define ON_SCOPE_EXIT \
    auto __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) = clover::detail::ScopeGuardOnExit() + [&]()

#else

// ScopeGuard for Objective-C
typedef void (^ext_cleanupBlock_t)(void);
static inline void ext_executeCleanupBlock(__strong ext_cleanupBlock_t *block) {
    (*block)();
}

#define ON_SCOPE_EXIT                                                              \
    __strong ext_cleanupBlock_t __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) \
        __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^

#endif

#endif /* __SCOPE_GUARD_H__ */

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值