最近在看programming ruby,里面涉及了闭包,百度中对闭包的解释为:
闭包是可以包含自由(未绑定)变量的代码块;这些变量不是在这个代码块或者任何全局上下文中定义的,而是在定义代码块的环境中定义。
闭包在现代语言中,变得越来越火热,对于java中是否要引入闭包功能,争论不休,C#不甘落后,已经引入了闭包,并将闭包应用在LINQ上。其他大部分常见的动态语言,都实现了闭包的处理,如ruby,python,JavaScript,等等,在这些语言中,闭包都是内建的。而其他的大量的静态语言(如C,C++),闭包功能,很难被引入或者很难实现。
从基于这些语言的实现考虑,大部分实现闭包处理的动态语言,都将函数作为对象看待,以我最熟悉的python来说,python内部对每一个函数,都看做一个类对象,甚至,基本类型也是对象。JavaScript的微软实现,所有的类型都是COM接口的具体实现,显然也是另外一个对象。这样做的好处是,当需要作为函数处理的时候,就将对象作为函数处理,当需要将函数作为类对象实例处理的时候,又可以做为类对象实例处理,从而简化了内部实现。
将函数作为对象看待,返回函数的名称,也就是函数对象,下面是的python代码中:
def counter(st=0):
count = [st]
def inc():
count[0] +=1
return count
return inc
counter函数和inc函数都是一个对象,同时也是一个函数。在counter对象中定义的函数inc,必然可以访问counter对象中的变量count,这是我们目前所有语言实现都满足的。inc是一个函数,同时也是一个对象,因此在counter返回inc的时候,返回的是inc对象的实例,该实例依赖于counter对象的初始化
通常我们会在函数外部,调用counter函数,通常的写法是:
c = counter(2)
从形式上看,这既是一个函数的调用,同时也是一个类对象初始化的调用。想想我们定义类的时候,类的实例化也是这么一种形式。那么c就是counter对象的一个实例。初始化这个实例的时候,count被初始化,然后是inc对象实例(这是一个匿名的实例)也被创建。然后这个创建的inc对象实例,引用了局部变量count的值,这个值在匿名的实例中被保存起来(如果找不到count,我们无法通过编译),当我们返回inc的时候,虽然我们在外部已经释放了count,但是由于这些内容是通过索引计数器保存在heap中的,因此这些值不会被释放。
也就是说在c = counter(2)的时候,系统进行了一些初始化工作,从而使得内部有一个inc的匿名实例被创建,通过return方法返回c,然后c调用的时候就是该函数的具体实现,这个时候,count的值再被取出来,进行处理。从而完成了闭包工作中重要的外部环境已经变化了,但内部环境仍旧保留着的这么一个重要功能。
我们通过打印id号来识别具体的内部变量的变化情况
#! /usr/bin/env python
#coding=utf-8
def counter(k):
m = k
print id(m)
def inc():
x = m
print id(x)
return x
print id(inc)
return inc
k = [12,23]
print id(k)
c = counter(k)
print id(c)
print id( c() )
print "-"*10
k2 = [12,34,56]
print id(k2)
c = counter(k2)
print id(c)
print id( c() )
输出的结果为:
19641248
19641248
19673904
19673904
19641248
19641248
----------
19672520
19672520
19673968
19673968
19672520
19672520
显然,结果与我们之前的分析是一致,也说明了闭包在python语言中的实现是这么一回事。
我们理解了闭包含义,那么C++中类似闭包的实现,就是实现仿函数,然后在仿函数内部实现一个子仿函数的内部实现,为了控制内存问题,下面是上面代码的一个类似实现,显然C++由于语言内部的不支持,编写闭包功能的函数更复杂,更不灵活:
#include <boost/shared_ptr.hpp>
#include <iostream>
using namespace std;
struct BaseFunction
{
public:
virtual int operator()()
{
return 0;
}
virtual ~BaseFunction()
{
}
};
struct CloseureFunction
{
public:
boost::shared_ptr<int> m;
struct ClosureInnerFunction : public BaseFunction
{
boost::shared_ptr<CloseureFunction> parent;
ClosureInnerFunction( boost::shared_ptr<CloseureFunction> parent )
: parent( parent )
{
}
private:
virtual int operator()()
{
int* p = parent->m.get();
*p += 2;
return *p;
}
};
public:
boost::shared_ptr<ClosureInnerFunction> operator()(boost::shared_ptr<CloseureFunction> pThis,
boost::shared_ptr<int> k)
{
m = k;
boost::shared_ptr<ClosureInnerFunction> p(new ClosureInnerFunction( pThis ) );
return p;
}
};
int main(int argc, _TCHAR* argv[])
{
boost::shared_ptr<int> m(new int(10) );
boost::shared_ptr<CloseureFunction> CF ( new CloseureFunction() );
boost::shared_ptr<BaseFunction> c = (*CF)( CF, m );
cout << (*c)() << endl;
return 0;
}
当然,boost::bind方法已经基本实现了闭包的类似功能。
基于闭包的代码实现,代码灵活度和重用性能更高,但代码相对也比较难以理解。与继承不一样的是,闭包更加松耦合与紧内聚。同时闭包的功能扩展也更加方便。