C++中闭包的使用

一.闭包的概念及起源

1.维基百科中的闭包概念

在计算机科学中,闭包(Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量(未绑定到特定对象)的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。所以,有另一种说法认为闭包是由函数和与其相关的引用环境组合而成的实体。闭包在运行时可以有多个实例,不同的引用环境和相同的函数组合可以产生不同的实例。

2. 历史由来

彼得·兰丁在1964年将术语“闭包”定义为一种包含环境成分和控制成分的实体,用于在他的SECD机器上对表达式求值。Joel Moses认为是Landin发明了“闭包”这一术语,用来指代某些其开放绑定(自由变量)已经由其语法环境完成闭合(或者绑定)的lambda表达式,从而形成了闭合的表达式,或称闭包。这一用法后来于1975年被Sussman和Steele在定义 Scheme语言的时候予以采纳。并广为流传。彼得·兰丁在1964年将术语“闭包”定义为一种包含环境成分和控制成分的实体,用于在他的SECD机器上对表达式求值。Joel Moses认为是Landin发明了“闭包”这一术语,用来指代某些其开放绑定(自由变量)已经由其语法环境完成闭合(或者绑定)的lambda表达式,从而形成了闭合的表达式,或称闭包。这一用法后来于1975年被Sussman和Steele在定义 Scheme语言的时候予以采纳。并广为流传。

3.闭包的简单理解
  • 对于维基百科中第一种说法,简单来说,闭包就是能够读取其他函数内部变量的函数(这是百度百科中对闭包的定义),即这个特殊的函数为闭包。
    这个特殊函数我见过的三种不同的说法:引用了自由变量的函数,带有上下文的函数,有状态的函数。
  • 第二种说法,简单来说就是认为其他函数内部变量+读取这个函数形成的整体为闭包。

相比较之下,第二种说法更为确切。

二.C++中闭包的实现

在不同的语言中闭包有不同的实现凡是,在C++中,闭包一般有三种实现方式。

  • 使用类重载()运算符定义函数对象兑现来实现闭包
  • 使用C++11标准引入的bind函数实现闭包(C中通过函数指针实现类似的操作)
  • 使用C++11标准引入的Lambda表达式来实现闭包
1.类中重载()运算符

通过在重载函数调用运算符中使用类的成员数据实现闭包,一般在C++中也称闭包为携带了状态的函数对象。外部变量即为函数对象的状态。
这种方式使用闭包比较繁琐,对于每一段闭包代码都要单独写一个函数对象类,所以C++11中引入了更为方便的方式。

class FunctionObject {
public:
    FunctionObject()
    : _count(0)
    {}
    int operator()(int x, int y) {
        cout << "operator()(int,int) " << endl;
        ++_count;
        return x + y;
    }
private:
    int _count; //函数对象的状态
};

int main() {
    int a = 3, b = 4, c = 5;
    FunctionObject fo;//本身是一个对象,这个对象即为一个闭包
    cout << "fo(a, b) = " << fo(a, b) << endl;//在形式上是一个函数的写法
    return 0;
}
2.bind函数

bind通过把外部变量函数绑定在一起实现闭包,bind返回值为一个函数对象,可使用类模板function接收
bind和function被定义在<functional>头文件中

#include <functional>

int func(int x, int y) {  
    return x + y;
}

int main() {
    int a = 3, b = 4;
    std::function(int(int)) f = std::bind(func, _1, b); //_1为占位符
    //f即为一个闭包,闭包f中包含局部变量b与函数func,在接下来对f的使用直接调用func函数和变量b
    cout << "f(a) = " << f(a) << endl;
    return 0;
}

//typedef int(*Function)(int,int); //函数指针
//Function f = func; //C通过函数指针实现类似操作
//f(a,b);
3.Lambda表达式(匿名函数)
  • Lambda表达式一般的声明格式如下:
// []内为匿名函数捕获外部变量的列表,()内为函数参数列表
[capture list] (params list) {function body}
[capture list] {function body}  //无参匿名函数

//使用实例
[] { cout << "Hello, World" << endl; };
  • Lambda表达式完整的声明格式如下:
[capture list] (params list) mutable exception-> return type { function body }
  • 由Lambda表达式创建闭包
    引用陈皓大神的看法:当匿名函数和non-local变量结合起来,就形成了闭包。
    实际上编译器是将Lambda表达式翻译成未命名类的未命名对象并重载了函数调用运算符,当Lambda表达式有捕获变量时(即这个对象有自己的数据)就形成了闭包。所以Lambda表达式就是第一种形式的简化写法。
int i = 1024;
auto func = [=] { printf("%d\n", i); }; //使用func接管这个匿名函数
//这里func即为一个闭包

三.C++中使用闭包的注意事项

C++闭包中保存了其代码内全部向外引用的变量的拷贝引用。如果是对外界环境中的对象的引用,且闭包执行时该外界环境的变量已经不存在(如在调用栈上已经展开),那么可导致未定义行为,因为C++并不扩展这些被引用的外界环境的变量的生命期。
翻译成白话文,就是闭包只使用这些变量,不管这些变量的生存周期

参考资料

  • 14
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值