C++实现Go的defer示例代码

在Go语言中有一个关键字:defer,它的作用就是延迟执行后面的函数,在资源释放方面特别有用,比如下面一段C/C++的示例代码:

void test()
{
	FILE* fp = fopen("test.txt", "r");
	if (nullptr == fp)
		return;

	if (...)
	{
		fclose(fp);
		return;
	}
	if (...)
	{
		fclose(fp);
		return;
	}
	if (...)
	{
		fclose(fp);
		return;
	}
	fclose(fp);
}

在每一处返回之前都需要调用fclose来关闭文件句柄,中间的流程中断越多,越是容易遗漏调用fclose导致未正常关闭文件。

C++可以使用shared_ptr,auto_ptr之类的智能指针来管理分配的内存,但是像上面这种情况C++并没有现成的可使用的代码来处理。而Go语言提供了defer关键字来解决此类问题,Go可以按如下方式来写:

func test() {
	file, err := os.Open("test.txt")
	if err != nil {
		return
	}
	defer file.Close()
	if ... {
		return
	}
	if ... {
		return
	}
	if ... {
		return
	}
}

只需要使用一句:

defer file.Close()

即可,Go会自动在return之后调用defer后面的函数。我们再看看下面的示例:

package main

import (
	"fmt"
)

func test() (n int, err error) {
	defer fmt.Println("测试1")
	defer fmt.Println("测试2")
	defer fmt.Println("测试3")
	return fmt.Println("test")
}

func main() {
	test()
}

它的输出为:

test
测试3
测试2
测试1

在这里插入图片描述

可以看出有多个defer时,按照先进后出的方式执行的。

C++中我们可以利用析构函数来实现,而且C++的局部变量析构规则也是按照先进后出的方式执行的。为此,我们需要定义一个Defer类:

#include <functional>
typedef std::function<void()> fnDefer;
class Defer
{
public:
	Defer(fnDefer fn) : m_fn(fn)
	{
	}
	~Defer()
	{
		if(m_fn)
			m_fn();
	}
private:
	fnDefer m_fn;
};

这样,前面的C++示例代码可以写成:

void test()
{
	FILE* fp = fopen("test.txt", "r");
	if (nullptr == fp)
		return;

	Defer d([&]()
	{
		fclose(fp);
	});
	if (...)
	{
		return;
	}
	if (...)
	{
		return;
	}
	if (...)
	{
		return;
	}
}

不用再在每一处返回前手动写代码关闭文件了。

但是这里还有一点不便之处就是需要手写一个lambda表达式和手动定义一个变量,这个很好解决,使用宏来处理。

#define defer1(a,b) a##b
#define defer2(a, b) defer1(a, b)
#define defer(expr) Defer defer2(__Defer__,__COUNTER__) ([&](){expr;})

为了方便在同一函数多处使用,定义了defer宏来给变量命不同的名,前面的代码可以改为:

void test()
{
	FILE* fp = fopen("test.txt", "r");
	if (nullptr == fp)
		return;

	defer(fclose(fp));
	if (...)
	{
		return;
	}
	if (...)
	{
		return;
	}
	if (...)
	{
		return;
	}
}

这样就实用且方便得多了。下面给出完整代码以及测试用例:

#include <functional>

using namespace std;

typedef std::function<void()> fnDefer;
class Defer
{
public:
	Defer(fnDefer fn) : m_fn(fn)
	{
	}
	~Defer()
	{
		if(m_fn)
			m_fn();
	}
private:
	fnDefer m_fn;
};

#define defer1(a,b) a##b
#define defer2(a, b) defer1(a, b)
#define defer(expr) Defer defer2(__Defer__,__COUNTER__) ([&](){expr;})

class Test
{
public:
	void f(int i)
	{
		printf("f:%d %p\n", i, this);
	}
};

int main(int argc, char *argv[])
{
	Test t;
	printf("test:%p\n", &t);
	defer(t.f(1));
	defer(t.f(2));
	defer(t.f(3));

	return 0;
}

结果如下:

在这里插入图片描述

以上在VC 2015以及GCC、Clang下测试通过。

关于C++实现Go的defer功能(示例代码)的文章就介绍至此,更多相关Go关键字defer内容请搜索编程宝库以前的文章,希望大家多多支持编程宝库

 [LeetCode] 8. String to Integer (atoi) 字符串转为整数Implementatoiwhichconverts a string to an in ...