如果你学过GO语言,那对defer就不陌生了,LPC语言中的defer和GO语言中的defer非常相似。
defer 的作用和执行时机
defer() 函数是用来延迟执行函数的,而且延迟发生在调用函数 return 之后,使用语法是:
void defer(function f);
我们看一个例子:
int main(object me, string arg)
{
defer(function() {
write("defer!\n");
});
write("test!\n");
return 1;
}
输出结果是:
test!
defer!
这里defer的函数在return后才调用,注意这里使用的是匿名函数,效果和函数指针(: write, "defer!\n" :)一样。
defer 的重要用途:清理释放资源
由于 defer 的延迟特性,defer 常用在函数调用结束之后清理相关的资源,比如数据库操作结束后关闭连接,文件资源的释放会在函数调用结束之后借助 defer 自动执行,不需要时刻记住哪里的资源需要释放。
我们看以下代码:
void db()
{
mixed db_handle, db_res;
db_handle = db_connect("", "/debug.sqlite.db", "", __USE_SQLITE3__);
/* error */
if (stringp(db_handle))
return db_handle;
db_res = db_exec(db_handle, "select * from users");
/* error */
if (stringp(db_res))
{
// 释放资源
db_close(db_handle);
return;
}
for (int i = 1; i <= db_res; i++)
{
printf("%O\n", db_fetch(db_handle, i));
}
// 释放资源
db_close(db_handle);
}
这里写的比较简单,但我们得记住在哪儿需要释放资源,而且很有可能在报错时直接return忘了释放,如果条件比较复杂,出错的可能会更大,而我们用defer解决看看:
void db()
{
mixed db_handle, db_res;
db_handle = db_connect("", "/debug.sqlite.db", "", __USE_SQLITE3__);
/* error */
if (stringp(db_handle))
return db_handle;
// 保证在结束后释放资源
defer((: db_close, db_handle :));
db_res = db_exec(db_handle, "select * from users");
if (stringp(db_res))
{
return;
}
for (int i = 1; i <= db_res; i++)
{
printf("%O\n", db_fetch(db_handle, i));
}
}
多个 defer 的执行顺序
defer 的作用就是把函数执行压入一个栈中延迟执行,多个 defer 的执行顺序是后进先出,我们看看以下代码:
int main(object me, string arg)
{
defer((: write,"defer 1\n" :));
defer((: write,"defer 2\n" :));
defer((: write,"defer 3\n" :));
write("test defer!\n");
return 1;
}
输出结果:
test defer!
defer 3
defer 2
defer 1
被 deferred 函数的参数在 defer 时确定
这是 defer 的特点,一个函数被 defer 时,它的参数在 defer 时进行计算确定,即使 defer 之后参数发生修改,对已经 defer 的函数没有影响,什么意思?看例子:
int main(object me, string arg)
{
int i = 1;
defer((: printf, "i1 = %i\n", i++ :));
defer((: printf, "i2 = %i\n", i++ :));
printf("i = %O\n", i);
return 1;
}
输出结果:
i = 3
i2 = 2
i1 = 1
函数运行时错误不影响 defer
如果函数运行时报错,defer也有正常执行,看看以下示例:
int main(object me, string arg)
{
defer((: write,"defer 1\n" :));
defer((: write,"defer 2\n" :));
defer((: write,"defer 3\n" :));
error("出错啦~");
write("test defer!\n");
return 1;
}
这里显示的抛出了错误,后面的write("test defer!\n");不会被执行,但 defer 不受影响,输出结果:
//这里是报错信息
defer 3
defer 2
defer 1
利用这个特性,我们可以在程序出错时回滚相关操作。
小结
根据以上内容,我们可以总结 defer 的几个重要知识点:
- 延迟对函数进行调用;
- 即时对函数的参数进行求值;
- 根据 defer 顺序反序调用;
- 代码报错不中断defer执行;
更多关于defer的应用欢迎留言讨论~