go語言 defer 你不知道的秘密!


go 語言的defer功能強大,對於資源管理非常方便,但是如果沒用好,也會有陷阱哦.我們先來看幾個例子.

例一: defer 是先進后出

  這個很自然,后面的語句會依賴前面的資源,因此如果先前面的資源先釋放了,后面的語句就沒法玩了.

1 func main() {
2     var whatever [5]struct{}
3 
4     for i := range whatever {
5         defer fmt.Println(i)
6     }
7 }

這個輸出應該很明顯,就是4 3 2 1 0

例二: defer 碰上閉包

func main() {
    var whatever [5]struct{}
    for i := range whatever {
        defer func() { fmt.Println(i) }()
    }

}

這個輸出可能會超出某些人的意料,結果是4 4 4 4 4

其實go說的很清楚,我們一起來看看go spec如何說的

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked. 

也就是說函數正常執行,由於閉包用到的變量i在執行的時候已經變成4,所以輸出全都是4.

例三: defer f.Close

這個大家用的都很頻繁,但是go語言編程舉了一個可能一不小心會犯錯的例子.

type Test struct {
    name string
}
func (t * Test) Close(){
    fmt.Println(t.name," closed");
}
func main(){
    ts:=[]Test{{"a"},{"b"},{"c"}}
    for _,t := range ts{
        defer  t.Close()
    }
}

這個輸出並不會像我們預計的輸出c b a,而是輸出c c c

可是按照前面的go spec中的說明,應該輸出c b a才對啊.

那我們換一種方式來調用一下.

例四: 像例一一樣的調用

type Test struct {
    name string
}
func (t * Test) Close(){
    fmt.Println(t.name," closed");
}
func Close(t Test){
    t.Close()
}
func main(){
    ts:=[]Test{{"a"},{"b"},{"c"}}
    for _,t := range ts{
        Close(t)
    }
}

這個時候輸出的就是c b a

當然,如果你不想多寫一個函數,也很簡單,可以像下面這樣,同樣會輸出c b a

例五:看似多此一舉的聲明

type Test struct {
    name string
}
func (t * Test) Close(){
    fmt.Println(t.name," closed");
}
func main(){
    ts:=[]Test{{"a"},{"b"},{"c"}}
    for _,t := range ts{
        t2:=t
        t2.Close()
    }
}

通過以上例子,結合

Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usualand saved anew but the actual function is not invoked. 

這句話.可以得出下面的結論:

defer后面的語句在執行的時候,函數調用的參數會被保存起來,但是不執行.也就是復制了一份.但是並沒有說struct這里的this指針如何處理,通過這個例子可以看出go語言並沒有把這個明確寫出來的this指針當作參數來看待.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM