关于Golang的闭包问题

关于Go的闭包实现有点疑惑,特来请教.先贴代码:

package main

import "fmt"

func main() {
    // 测试一:
    adderCountF1 := adderCount()
    fmt.Println(adderCountF1(1))  // 0 += 1
    fmt.Println(adderCountF1(2))  // 1 += 2

    adderCountF2 := adderCount()
    fmt.Println(adderCountF2(1))  // 0 += 1
    fmt.Println(adderCountF2(2))  // 1 += 2

    // 测试二:
    var x int
    func(max int) {
        sum := 0
        for i := 0; i < max; i++ {
            sum += i
        }
        x = sum
    }(100)
    fmt.Println(x)  // 4950
}

func adderCount() func(int) int {
    var x int
    return func(delta int) int {
        x += delta
        return x
    }
}

以下是我的理解:
基于测试一,我认为闭包引用外部函数变量时是将外部变量的值在内部进行了拷贝
基于测试二,我发现内部变量与外部引用的是相同变量

问题:
以上两个测试我的理解互相矛盾.我猜测Golang是不是有类似函数实例的概念?
上面这两种闭包的行为编译器会怎么解释?

阅读 4.9k
5 个回答
  1. 函数实例这个概念是类似于类的实例么?肯定的话,那么答案就是.

  2. 关于编译器的问题我不懂,闭包大概就是函数执行的内存包含了一个环境,期待大牛的解答。

1.关于测试二:闭包就是引用的相同的变量,可以通过打印x的地址得到证明

2.关于测试一:为什么运行adderCountF1(1)与adderCountF2(1)输出结果一样呢?

原因很简单,语句adderCountF1 := adderCount()与语句adderCountF2 := adderCount()得到的是两个不同的对象,可以理解是用一个类new出来的两个不同对象,可以通过fmt.Println(&adderCountF1==&adderCountF2)验证

对象都不是同一个,所以才会有测试一那样的输出结果。

实际上语句

func adderCount() func(int) int {
    var x int
    return func(delta int) int {
        x += delta
        return x
    }
}

仅仅是定义了函数而已,它在内存中并没有实际的空间,只有在调用的时候才会在内存中生成对象,调用几次生成几次,得到的当然是不同的对象,互不影响。

你既然把 x 放在了 adderCount 的里面,为什么觉得每次调用 adderCount 的时候都是同一个 x,我很好奇。

测试1中:adderCount()方法返回func.是golang类型之一.仅仅是一个type 为 func 的返回值.和int之类的性质是一样的。这样你在adderCountF1 := adderCount() 相当与获取了一个新的返回值。所以在执行adderCountF2 := adderCount()的时候。其内部的sum是0。至于测试2.闭包特性就是引用外部数据。你在闭包中做的操作自然是直接操作的外部数据x。

可以这样理解,如果闭包内引用了一个局部变量,那么编译器这个变量由栈变量提升为堆变量,那么实际上测试一和测试二的var x 都是运行时在堆上分配的。
测试一中,由于调用了两次adderCount() 方法,因此每次调用会分配一个新的 x,其初始值都是0,所以两次执行结果一致。这与测试二中的得到的结果并不矛盾。

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题