C++实现Go的defer功能(示例代码)

时间:2022-09-19 08:24:03

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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可以按如下方式来写:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
func test() {
    file, err := os.Open("test.txt")
    if err != nil {
        return
    }
    defer file.Close()
    if ... {
        return
    }
    if ... {
        return
    }
    if ... {
        return
    }
}

只需要使用一句:

?
1
defer file.Close()

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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

C++实现Go的defer功能(示例代码)

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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++示例代码可以写成:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void test()
{
    FILE* fp = fopen("test.txt", "r");
    if (nullptr == fp)
        return;
 
    Defer d([&]()
    {
        fclose(fp);
    });
    if (...)
    {
        return;
    }
    if (...)
    {
        return;
    }
    if (...)
    {
        return;
    }
}

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

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

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

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void test()
{
    FILE* fp = fopen("test.txt", "r");
    if (nullptr == fp)
        return;
 
    defer(fclose(fp));
    if (...)
    {
        return;
    }
    if (...)
    {
        return;
    }
    if (...)
    {
        return;
    }
}

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

?
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
36
37
38
39
40
41
42
43
#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;
}

结果如下:

C++实现Go的defer功能(示例代码)

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

到此这篇关于C++实现Go的defer功能(示例代码)的文章就介绍到这了,更多相关Go关键字defer内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/witton/article/details/118573922