C++11实现defer

问题提出

使用过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
open file ./test.log

② 当文件存在时:

1
2
open file ./test.log
close file ./test.log
感谢您对本站的支持.