Go中defer语句
Go语言的 defer 语句会将其后面跟随的语句进行延迟处理,在 defer 归属的函数即将返回时,将延迟处理的语句按 defer 的逆序进行执行,也就是说,先被 defer 的语句最后被执行,最后被 defer 的语句,最先被执行。
典型的使用场景就是使用延迟执行语句在函数退出时释放资源。
处理业务或逻辑中涉及成对的操作是一件比较烦琐的事情,比如打开和关闭文件、接收请求和回复请求、加锁和解锁等。在这些操作中,最容易忽略的就是在每个函数退出处正确地释放和关闭资源。
C++实现延迟执行原理
- 变量离开其生命周期就会被销毁。
- 类的实例在被销毁前会调用其析构函数。
- 将需要延迟执行的语句保存在类的实例中,在析构函数中调用延迟执行语句。
代码实现
#pragma once
template <typename F> class Defer;
template <typename F> Defer<F> make_defer(F f);
// Defer的辅助宏定义,避免函数内多次使用defer造成变量名相同,导致编译失败。
#define DEFER_NAMELINE_CAT(name, line) name##line
#define DEFER_NAMELINE(name, line) DEFER_NAMELINE_CAT(name, line)
#define defer(F) auto DEFER_NAMELINE(DEFER_NAME_, __LINE__) = make_defer(F)
template <typename F> class Defer
{
public:
Defer(Defer &&other) noexcept
: m_func(std::move(other.m_func))
, m_invoke(other.m_invoke)
{
other.dismiss();
}
Defer &operator=(Defer &&other)
{
m_func = std::move(other.m_func);
other.dismiss();
}
~Defer()
{
if (m_invoke) {
m_func();
}
}
void dismiss() noexcept
{
m_invoke = false;
}
// 删除拷贝构造和拷贝赋值,避免延迟语句在变量赋值后被多次调用。
Defer(const Defer &) = delete;
Defer &operator=(const Defer &) = delete;
private:
explicit Defer(F f) noexcept
: m_func(std::move(f))
{
}
F m_func;
bool m_invoke = true;
friend Defer make_defer<F>(F);
};
// Defer的辅助函数,实际构造Defer类实例的地方
template <typename F> Defer<F> make_defer(F f)
{
return Defer<F>(std::move(f));
}
使用方法
void complexCodeWithMultipleReturnPoints(int v)
{
// lambda表达式为需要延迟执行的语句。
// 无论函数在哪个异常分支或正常流程退出,都会执行此lambda表达式。
defer([]{ code_that_you_want_exec(); });
if (v == -1) { return;}
int v2 = code_that_might_throw_exceptions();
if (v2 == -1) { return;}
...
return;
}