defer是golang中的延迟调用函数,在return之后调用,但实际运用中还是比较难理解的。
理解1:传给defer模块函数的实参会在定义defer时生成备份
如果内部使用的值不是通过形参传递,比如直接引用defer模块外部已经定义好的值,则不会生成备份。
仔细看下面三个函数:
package main
import "fmt"
func c() {
i := 0
defer fmt.Println("c1:",i)
i++
defer fmt.Println("c2:",i)
}
func cc() {
i := 0
defer func() {
fmt.Println("cc1:",i)
}()
i++
defer func() {
fmt.Println("cc2:",i)
}()
}
func ccc() {
i := 0
defer func(t int) {
fmt.Println("ccc1:",t)
}(i)
i++
defer func(t int) {
fmt.Println("ccc2:",t)
}(i)
}
func main() {
c()
cc()
ccc()
}
输出结果为:
c2: 1
c1: 0
cc2: 1
cc1: 1
ccc2: 1
ccc1: 0
函数c()中,c1虽然比c2晚执行,但是c1的结果是小于c2的,这可以看出是c1所在模块定义时,传给该模块的i值是做了备份的。
而函数cc()中,cc1与cc2所在的模块都会在i++之后执行,且模块内的i不是通过形参的方式传入,所以没有生成备份,所以在i++之前或者之后定义这两个defer模块都是一样的效果。
函数ccc()的输出结果与函数c()是一样的,只不过用了一种更容易理解的方式。
函数cc()与函数c()的比较,可以看出,只有传参数给函数才会出现备份效果,直接引用模块外部变量是不会备份的。
通过函数ccc()与函数c()的比较可以看出,各种函数只要通过参数引用的方式传入defer模块,都会生成备份。
理解2:如果在defer内部操作“具名返回值”,是会对返回结果产生影响的
具名返回值,是不是又叫有名返回值???不清楚,我是在B站上学的go语言…
func foo1() int {…}
func foo2() (ret int) {…}
函数foo2()中的返回值已经定义好了,是一个名为ret的int类型的值(具名返回值),如果在defer模块中操作ret,最终函数调用者接收到的返回值不是return时返回的值,而是defer操作后的值。
foo1()中的返回值就是return时返回的值了,不用考虑defer模块中做过什么修改。
看如下代码,先主要看a()与b()两个函数:
package main
import "fmt"
func a() int {
i := 0
defer func() {
i++
fmt.Println("a:",i)
}()
i++
return i
}
func aa() int {
i := 0
i++
ret := i
func() {
i++
fmt.Println("aa:",i)
}()
return ret;
}
func b() (i int) {
i = 0
defer func() {
i++
fmt.Println("b:",i)
}()
i++
return i
}
func bb() (i int) {
i = 0
i++
func() {
i++
fmt.Println("bb:",i)
}()
return i
}
func main() {
fmt.Println("m:",a())
fmt.Println("m:",b())
}
输出:
a: 2
m: 1
b: 2
m: 2
函数a()的执行顺序实际可以用函数aa()来解释,在函数a()中执行return时,会先定义一个返回变量值ret,然后再去执行defer模块,最后ret会被传回给函数调用者。
如果ret在defer模块中被修改了,函数调用者接收到的值也跟着被修改,就像函数b()中的i已经事先被申明成了返回值,所以执行顺序和bb()是相同的,i的值会在defer中被修改,所以最终函数调用者接收到的值也和a()的不同。
总结
defer的其它特性就不说了,像先定义后执行之类的。
第一次接触defer这个模块时,感觉它和析构函数有点类似。
写得好啰嗦,其它没了