2012-06-21 6 views
19

は私が機能golangポインタが、それはメートル、お菓子、そして犬が渡されたことにより、参照されていることは事実です

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

//if (complicated thing) add Cat to m 

} 

を持っているパラメータ、およびmeowIdは、それが値がコピーされています持っています。

mはマップなので、その参照渡し。

犬は構造体です。だから、私はデータをコピーしないようにポインタを渡す必要があります。ここで定義されているよう

セット、インターフェースです:

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はメソッドを持っていながらタイプWhateverは、唯一の方法Bar()を持っている私は何を意味することは、以下を持っている場合、ありますFoo()およびBar()

type Grits interface { 
    Foo() 
    Bar() 
} 

WhateverメソッドFoo()がないため、その後*Whateverは、Gritsを実装していますがWhateverません:それはあなたが次のインタフェースを持っている場合を意味します。関数への入力をインタフェース型として定義するときは、それがポインタか値型かどうかはわかりません。

次の例では、両方の方法でインターフェイスタイプをとる関数を示しています

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) 
} 

*CandyRenableを実装しCandyはそうではないFruit*Fruitの両方が、Renamableを実装するので、あなたは、Raname(&f, "Jorelli Fruit")なくRename(c, "Jorelli Bar")を呼び出すことができます。関数呼び出しで

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 

trueの「参照渡し」があった場合、2番目の例では元のマップが変更されます。

ポインタを渡すと、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のコピーを作成し、 ポインタ値を渡すと、ポインターのコピーが作成されますが、ポインターのコピーは になります。 (これは 受信機の方法をどのように影響するかについての議論は、次のセクションを参照してください)

マップとスライスの値はポインタのように振る舞う:彼らは、基礎となるマップやスライスデータへのポインタを含む 記述子です。マップまたは スライス値をコピーしても、そのスライス値が指すデータはコピーされません。インターフェイス をコピーすると、インターフェイス値に格納されているもののコピーが作成されます。 インタフェースの値が構造体を保持している場合、インタフェースの値をコピーすると構造体のコピーが になります。インタフェースの値がポインタを保持している場合は、 をコピーするとポインタのコピーが作成されますが、ポインタのコピーはポインタ のデータではありません。

関連する問題