在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下测试通过。