golang 50度灰里面的"for"声明中的迭代变量和闭包的问题 不解

markcheney · · 1334 次点击
golwei
没有永恒.
1.是多线程读取单一地址内容.(要做同步) ```go package main import ( "fmt" ) type field struct { name string } func (p *field) print(c chan int) { fmt.Println(p.name) c <- 1 } func main() { data := []field{{"one"}, {"two"}, {"three"}} ch := make(chan int) for _, v := range data { go v.print(ch) <-ch } } ```
#10
更多评论
虽然楼主已经知道原因了,但是原因最好还是注明下,方便解惑他人。 先声明为了方便起见,我把指针称为地址,这样会更好理解一些。 先说下案例一 问题的关键在于print方法的声明,现在是func (p *field) print()。 1、首先明确一点,在for循环结束之后,复用变量v的值其实已经变成了three。 2、其次注意print方法的声明,func (p *field) print(),细心的同学发现了,print方法的接收者其实是一个地址。 3、for循环中,我们传递给print的方法接收者是对象v,但是方法接收者却要求是一个地址,这时,golang自动做了转换,把对象的地址传递过去了,注意是对象的地址传递过去了,而不是对象的值,参考第一条,因为v是复用变量,v的地址是不会变,但v的值会变,由one变成two,由two变成three,既然我们3次传递的都是v的地址,所以打印出来的自然是同一个值 再说下案例二 案例二中,print方法的声明依然是func (p *field) print(),但是打印出来的却是one,two,three,这是为何呢? 原因就是在于这次我们传递不再是一个对象,而是一个对象地址,这个时候,我们传递的数据类型和方法声明的类型是同一个类型,这个时候,可以理解为golang不会对参数进行转换,而是直接拷贝了传递过来的地址,既然每次传递过来的地址都是不一样的,那么打印出来的值自然不同 如何让案例一也输出one,two,three呢? 修改print方法的声明,func (p *field) print()改成func (p field) print(),我们把方法接收者改成对象,而不是对象的地址,,因为for中,每次传递过去的值都是不一样的,这个时候,打印出来的自然就是one,two,three。不建议这么做,原因在下面会说明。 说了那么多,总结一句话,注意传递给方法的接收者类型是不是该方法声明的类型,不一样的话,自然会出现一些意想不到的效果,最好在编码的时候,注意这点。 还有一点说明下上面为什么说不建议修改print方法的声明,案例一中,for循环其实是在拷贝数组中的每一个元素给v,这样内存是默默的一次次的无情消耗,所以,最好for循环遍历是指针数组,而不是对象数组。最好的方式是data就是一个指针数组,这样和方法声明一致。而且for循环中也不会产生大量的隐形拷贝行为。
#2