zoukankan      html  css  js  c++  java
  • 闭包初探

    最近在看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

    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方法已经基本实现了闭包的类似功能。

    基于闭包的代码实现,代码灵活度和重用性能更高,但代码相对也比较难以理解。与继承不一样的是,闭包更加松耦合与紧内聚。同时闭包的功能扩展也更加方便。

     
  • 相关阅读:
    C++静态库与动态库(转)
    Tornado异步
    Yacc与Lex
    云数据库
    linux如何查看端口被谁占用
    Innodb Double Write
    MySQL GTIDs(global transaction identifiers)
    Java并发编程:线程池的使用
    Oracle 建立索引及SQL优化
    解决redhat linux下IP地址可以ping通,域名无法ping通问题
  • 原文地址:https://www.cnblogs.com/ubunoon/p/1734342.html
Copyright © 2011-2022 走看看