问题提出
使用过swift开发过iOS应用的广大搬砖工都知道, 在swift语言里, 有一个很好用的关键字 defer
这个defer有什么用呢?
在某个函数或者代码块中, 如果使用了defer 添加了一段代码, 会发生什么呢?
哈哈, 当然是 在代码块结束时, 一定会调用这段代码了。
这样有什么好处呢?
我们知道, 有些操作需要很多步骤,其中一步出错,就可能需要是整个操作流程结束, 但是结束之前那些已经申请的资源怎么办呢? 如果不处理肯定会出现资源泄露的啊。这是defer就派上用场了,使用defer 之后, 在后面的代码块中去释放申请占用的资源, 就会保证在代码块结束之前一定会调用这个defer代码块来释放资源, 保证了一定的安全性
问题思路
那作为一名C++开发者来说,有没有这个便利的工具呢?
老实说, 从原生C++角度来说, 是没有这个工具的,有点可惜! 那我们能不能自己实现呢?
思路分析
1. defer 功能是退出代码块时必定会执行代码, C++里面有这种机制吗? 当然有啊, 局部对象的析构函数, 可行性 +1
2. defer 后面接的是代码块, C++11以后, 也有了lambda表达式的支持, 可行性再次 +1
3. defer 后面所接的代码块是不带括号尾部闭包格式, C++ 有什么方式可以以函数对象为参数且不需要括号就能调用的呢? 哈哈, 有, + - * / << >> += -= 等等操作符调用时不需要加括号, 可行性再次 +1
如何实现
- 需要定义一个类
- 这个类可以通过调用操作符的方式往内部追加一个代码块
- 该类的对象析构时, 一定会去调用内部保存的代码块
动手试试
不如我们就定义一个类叫Defer好了, 至于用哪个操作符呢? 凭自己喜好啊, 我这里使用了 += 表示往里头追加, 哈哈
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| #include <vector> #include <functional> struct Defer { Defer& operator+=(std::function<void()> block) { m_defers.emplace_back(std::move(block)); return *this; }
~Defer() { while (!m_defers.empty()) { if(m_defers.back()) m_defers.back()(); m_defers.pop_back(); } } private: std::vector<std::function<void()>> m_defers; };
#define USE_DEFER Defer __defer__ #define defer __defer__ +=
|
测试看看?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include <iostream> int main(int argc, char **argv) { USE_DEFER;
auto a = new int(10); std::cout << "new a" << std::endl;
defer [=] { std::cout << "delete a" << std::endl; delete a; };
auto b = new char[100]; std::cout << "new [] b" << std::endl;
defer [=] { std::cout << "delete [] b" << std::endl; delete [] b; };
return 0; }
|
输出结果是不是预期的呢?
1 2 3 4
| new a new [] b delete [] b delete a
|
哈哈, 还真是!! 功能实现了
更多
这里的 defer 还能用在类中, 确保资源在类对象析构的时候能正确的释放资源
比如
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| class File { public: File(std::string path) : m_path(std::move(path)) { defer [=] { if(m_descriptor > 0) { std::cout << "close file " << m_path << std::endl; ::close(m_descriptor); m_descriptor = -1; } }; }
void open() { std::cout << "open file " << m_path << std::endl; m_descriptor = ::open(m_path.c_str(), O_RDONLY); }
private: USE_DEFER; std::string m_path; int m_descriptor{-1}; };
int main(int, char**) {
File file("./test.log");
file.open();
return 0; }
|
预期结果
① 当文件不存在时:
② 当文件存在时:
1 2
| open file ./test.log close file ./test.log
|