LPC语言小知识:defer函数的使用

如果你学过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的应用欢迎留言讨论~

京ICP备13031296号-4