2012-06-21 62 views
19

我具備的功能golang指針參數

func addCatsToMap(m map[string][]CatHouse, meowId int, treats Set, dog *Dog) { 

//if (complicated thing) add Cat to m 

} 

這是真的,男,治療,和狗傳遞按引用,並meowId有它的價值被複制。

由於m是map,所以它是通過引用的。

狗是一個結構。所以,我應該通過指針來避免複製數據。

設置是一個接口,這裏定義的:

type Set interface { 
    Add(value string) 
    Contains(value string) (bool) 
    Length() (int) 
    RemoveDuplicates() 
} 

是通過設置按值?

回答

37

接口類型只是一組方法。請注意,接口定義的成員不指定接收器類型是否是指針。這是因爲值類型的方法集是其關聯的指針類型的方法集的子集。這是一口。我的意思是,如果你具備以下條件:

type Whatever struct { 
    Name string 
} 

,並定義了以下兩種方法:

func (w *Whatever) Foo() { 
    ... 
} 

func (w Whatever) Bar() { 
    ... 
} 

然後類型Whatever只有方法Bar(),而式*Whatever有方法Foo()Bar()。這意味着如果你有以下接口:

type Grits interface { 
    Foo() 
    Bar() 
} 

然後*Whatever實現GritsWhatever不會的,因爲Whatever缺乏方法Foo()。當您將輸入定義爲接口類型時,您不知道它是指針還是值類型。

下面的例子說明一個函數,這兩種方式的接口類型:

package main 

import "fmt" 

type Fruit struct { 
    Name string 
} 

func (f Fruit) Rename(name string) { 
    f.Name = name 
} 

type Candy struct { 
    Name string 
} 

func (c *Candy) Rename(name string) { 
    c.Name = name 
} 

type Renamable interface { 
    Rename(string) 
} 

func Rename(v Renamable, name string) { 
    v.Rename(name) 
    // at this point, we don't know if v is a pointer type or not. 
} 

func main() { 
    c := Candy{Name: "Snickers"} 
    f := Fruit{Name: "Apple"} 
    fmt.Println(f) 
    fmt.Println(c) 
    Rename(f, "Zemo Fruit") 
    Rename(&c, "Zemo Bar") 
    fmt.Println(f) 
    fmt.Println(c) 
} 

你可以調用Raname(&f, "Jorelli Fruit")但不Rename(c, "Jorelli Bar"),因爲這兩個Fruit*Fruit實施Renamable,而*Candy實現RenableCandy不。

http://play.golang.org/p/Fb-L8Bvuwj

6

通過引用傳遞是語言的東西,Go中沒有什麼是「通過引用傳遞」。按引用傳遞意味着分配操作員可以在單獨使用時更改原始值。但是,還有一些引用類型,如地圖和指向某處的指針。使用它們上的賦值運算符將不會修改原始值,除非您使用其他運算符(如地圖索引和運算符)。

你是正確的,你的地圖m是一個引用類型,因此像一個指針。除了替換地圖之外,對地圖的任何更改都會修改原始地圖。

m["whatever"] = 2   // Modifies the original map 
m = anothermap    // Does not modify the original map 

如果存在真正的「通過引用」,則第二個示例將修改原始映射。

傳遞一個指針,就像您使用dog一樣,您可以修改原件。如果您調用任何指針方法或使用運算符,則原始數據將會更改。在你的例子中,一個指針可能不需要。如果Dog很小,則傳遞副本可能會更容易。由程序員決定什麼時候使用指針是個好時機。

Set未通過引用傳遞。 接口不是引用。雖然6g編譯器內部的一個接口使用指針,但接口本身並不像一個接口。傳遞一個接口,不管它包含的對象的大小,都像使用6g編譯器傳遞指針一樣便宜。但是,您無法像使用指針和地圖一樣修改接口的原始值。

儘管您無法修改傳遞的原始接口,但接口可以包含指針類型。在這種情況下,它就像狗指針那樣調用某些方法可以修改原始文件。對於你的特定的Set接口,我猜測它包含一個基於方法名稱的指針類型。所以當你撥打set.Add(whatever)時,它會改變原來的內部數據。

3

Calls, The Go Programming Language Specification

在一個函數調用,該函數值和參數在 通常的順序進行評估。評估完成後,呼叫 的參數將按值傳遞給函數,被調用的函數將開始執行 。當函數返回時,函數的返回參數通過值 傳回給調用函數。

When are function parameters passed by value? FAQ - The Go Programming Language.

如在C系列中的所有語言, 一切都在走的是按值傳遞。也就是說,一個函數總是得到正在傳遞的東西的一個 副本,就好像有一個賦值語句將值賦給參數一樣 。例如,將一個int值傳遞給一個函數 將生成int的一個副本,並且傳遞一個指針值作爲指針的副本,但不會指向 指向的數據。 (見的這如何影響方法 接收機的討論下一節。)

地圖和限幅值表現得像指針:它們是描述符 包含指向基礎映射或切片的數據。複製地圖或 切片值不會複製它指向的數據。複製接口 值將存儲在接口值中的東西的副本。如果接口值包含一個結構體,則複製該接口值將生成該結構體的一個 副本。如果接口值持有一個指針,則複製 該接口值會複製指針,但不會指向它所指向的 數據。