前述:
go 中defer 给出了一种,延时调用的方式来释放资源。但是对于C/C++去没有内置的这种属性。对于经常手动管理内存的C/C++有其是C程序员这种特性显得无比重要。这里给出了一种基于GCC/Clang 编译器扩展属性(cleanup)来模拟实现defer的一种方式。
头文件如下:
#ifndef __SCOPEGUARD_H__ #define __SCOPEGUARD_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 DEFER \ auto __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) = \ clover::detail::ScopeGuardOnExit() + [&]() #elif defined(__APPLE__) /* ScopeGuard for Objective-C */ typedef void (^ext_cleanupBlock_t)(void); static inline void ext_executeCleanupBlock(__strong ext_cleanupBlock_t *block) { (*block)(); } #define DEFER \ __strong ext_cleanupBlock_t __SCOPEGUARD_CONCATENATE(ext_exitBlock_, __LINE__) \ __attribute__((cleanup(ext_executeCleanupBlock), unused)) = ^ #elif defined(__linux__) || defined(__ANDROID__) #include <stdlib.h> /** * Linux(HOST) GCC does not support extension 'blocks' and keyword '__strong' * So, just use cleanup in plain way */ #define ON_SCOPE_EXIT(T, V, FUNC) T *V __attribute__((cleanup(FUNC))) #define AUTOFREE(T, V) ON_SCOPE_EXIT(T, V, __autofree) void __autofree(void *arg) { printf("in cleanup\n"); void *ptr = *(void **)arg; if (ptr) { free(ptr); } } #endif #endif //__SCOPEGUARD_H__
测试:
不需要过多的解释,直接看执行结果就知道了。当超出作用域(break,goto,return)之后,会自动调用指定的内存释放函数(当然这里也可以用其他函数来代替)。()
C++:
#include <iostream>
#include "scopeguard.h" using namespace std; int main() { string *as = new string("hello world!"); DEFER { cout << "in scope guard" << endl; delete as; }; cout << "at end" << endl; return 0; }
$ c++ xx.c
$ ./a.out
at end
in scope guard
C:
#include <stdio.h>
#include "scopeguard.h" int main(void) { { AUTOFREE(int, pint) = (int *)malloc(4); *pint = 12; } printf("will exit in main\n"); return 0; } $ cc xx.c $ ./a.out in cleanup will exit in main
参考:
https://zhuanlan.zhihu.com/p/21303431
https://zhuanlan.zhihu.com/p/35191739