C++实现Go的defer功能
在Go语言中有一个关键字:defer,它的作用就是延迟执行后面的函数,在资源释放方面特别有用,比如下面一段C/C++的示例代码:
1void test()
2{
3 FILE* fp = fopen("test.txt", "r");
4 if (nullptr == fp)
5 return;
6
7 if (...)
8 {
9 fclose(fp);
10 return;
11 }
12 if (...)
13 {
14 fclose(fp);
15 return;
16 }
17 if (...)
18 {
19 fclose(fp);
20 return;
21 }
22 fclose(fp);
23}
在每一处返回之前都需要调用fclose来关闭文件句柄,中间的流程中断越多,越是容易遗漏调用fclose导致未正常关闭文件。
C++可以使用shared_ptr,auto_ptr之类的智能指针来管理分配的内存,但是像上面这种情况C++并没有现成的可使用的代码来处理。而Go语言提供了defer关键字来解决此类问题,Go可以按如下方式来写:
1func test() {
2 file, err := os.Open("test.txt")
3 if err != nil {
4 return
5 }
6 defer file.Close()
7 if ... {
8 return
9 }
10 if ... {
11 return
12 }
13 if ... {
14 return
15 }
16}
只需要使用一句:
1defer file.Close()
即可,Go会自动在return之后调用defer后面的函数。我们再看看下面的示例:
1package main
2
3import (
4 "fmt"
5)
6
7func test() (n int, err error) {
8 defer fmt.Println("测试1")
9 defer fmt.Println("测试2")
10 defer fmt.Println("测试3")
11 return fmt.Println("test")
12}
13
14func main() {
15 test()
16}
它的输出为:
1test
2测试3
3测试2
4测试1
可以看出有多个defer时,按照先进后出的方式执行的。
C++中我们可以利用析构函数来实现,而且C++的局部变量析构规则也是按照先进后出的方式执行的。为此,我们需要定义一个Defer类:
1#include <functional>
2typedef std::function<void()> fnDefer;
3class Defer
4{
5public:
6 Defer(fnDefer fn) : m_fn(fn)
7 {
8 }
9 ~Defer()
10 {
11 if(m_fn)
12 m_fn();
13 }
14private:
15 fnDefer m_fn;
16};
这样,前面的C++示例代码可以写成:
1void test()
2{
3 FILE* fp = fopen("test.txt", "r");
4 if (nullptr == fp)
5 return;
6
7 Defer d([&]()
8 {
9 fclose(fp);
10 });
11 if (...)
12 {
13 return;
14 }
15 if (...)
16 {
17 return;
18 }
19 if (...)
20 {
21 return;
22 }
23}
不用再在每一处返回前手动写代码关闭文件了。
但是这里还有一点不便之处就是需要手写一个lambda表达式和手动定义一个变量,这个很好解决,使用宏来处理。
1#define defer1(a,b) a##b
2#define defer2(a, b) defer1(a, b)
3#define defer(expr) Defer defer2(__Defer__,__COUNTER__) ([&](){expr;})
为了方便在同一函数多处使用,定义了defer宏来给变量命不同的名,前面的代码可以改为:
1void test()
2{
3 FILE* fp = fopen("test.txt", "r");
4 if (nullptr == fp)
5 return;
6
7 defer(fclose(fp));
8 if (...)
9 {
10 return;
11 }
12 if (...)
13 {
14 return;
15 }
16 if (...)
17 {
18 return;
19 }
20}
这样就实用且方便得多了。下面给出完整代码以及测试用例:
1#include <functional>
2
3using namespace std;
4
5typedef std::function<void()> fnDefer;
6class Defer
7{
8public:
9 Defer(fnDefer fn) : m_fn(fn)
10 {
11 }
12 ~Defer()
13 {
14 if(m_fn)
15 m_fn();
16 }
17private:
18 fnDefer m_fn;
19};
20
21#define defer1(a,b) a##b
22#define defer2(a, b) defer1(a, b)
23#define defer(expr) Defer defer2(__Defer__,__COUNTER__) ([&](){expr;})
24
25class Test
26{
27public:
28 void f(int i)
29 {
30 printf("f:%d %p\n", i, this);
31 }
32};
33
34int main(int argc, char *argv[])
35{
36 Test t;
37 printf("test:%p\n", &t);
38 defer(t.f(1));
39 defer(t.f(2));
40 defer(t.f(3));
41
42 return 0;
43}
结果如下:
以上在VC 2015以及GCC、Clang下测试通过。
- 原文作者:Witton
- 原文链接:https://wittonbell.github.io/posts/2021/2021-07-08-C++实现Go的defer功能/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议. 进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。