21

私は次の機能を持っています:

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

//if (complicated thing) add Cat to m

}

ここSetで、のタイプはtreats、次の定義を持つインターフェースです。

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

質問:

、、、およびが参照によって渡されm、その値がコピーされているというのは本当ですか?treatsdogmeowId

私はそれを仮定します:

  • mこれはマップであるため、参照渡しです
  • dog構造体です。したがって、データのコピーを避けるためにポインタを渡す必要があります
4

3 に答える 3

43

インターフェイスタイプは、単に一連のメソッドです。インターフェイス定義のメンバーは、レシーバータイプがポインターであるかどうかを指定しないことに注意してください。これは、値型のメソッドセットが、関連するポインタ型のメソッドセットのサブセットであるためです。それは一口です。私が言いたいのは、あなたが以下を持っている場合です:

type Whatever struct {
    Name string
}

そして、次の2つのメソッドを定義します。

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

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

その場合、タイプWhateverにはメソッドのみが含まれBar()、タイプ*WhateverにはメソッドFoo()とが含まれBar()ます。つまり、次のインターフェイスがある場合です。

type Grits interface {
    Foo()
    Bar()
}

次に、*Whatever実装しますが、メソッドGritsWhateverないため、実装しません。関数への入力をインターフェイス型として定義する場合、それがポインタ型であるか値型であるかはわかりません。WhateverFoo()

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

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

于 2012-06-21T04:06:16.863 に答える
6

参照渡しは言語的なものであり、Goには「参照渡し」はありません。参照渡しとは、代入演算子を単独で使用する場合に元の値を変更できることを意味します。ただし、どこかを指すマップやポインタなどの参照型があります。それらに代入演算子を使用しても、マップインデックスや演算子などの他の演算子を使用しない限り、元の演算子は変更されません*

mマップが参照型であり、したがってポインターのようなものであることは正しいです。マップを置き換える以外のマップへの変更は、元のマップを変更します。

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

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

ポインタを渡すと、dog元のポインタを変更できます。ポインタメソッドを呼び出すか、*演算子を使用すると、元のメソッドが変更されます。あなたの例では、ポインタは必要なかったかもしれません。が小さい場合Dogは、コピーを渡す方が簡単な場合があります。ポインターを使用するのに適した時期を判断するのは、プログラマーの責任です。

Set参照によって渡されません。インターフェイスは参照ではありません。6gコンパイラの内部では、インターフェイスがポインタを使用することは事実ですが、インターフェイス自体はポインタのようには機能しません。インターフェイスの受け渡しは、含まれるオブジェクトのサイズに関係なく、6gコンパイラを使用してポインタを渡すのと同じくらい安価です。ただし、ポインタやマップを使用した場合のように、インターフェイスの元の値を変更する方法はありません。

渡された元のインターフェイスを変更することはできませんが、インターフェイスにはポインタ型を含めることができます。その場合、特定のメソッドを呼び出すと元のメソッドを変更できるドッグポインターのように機能します。あなたの特定のSetインターフェースでは、メソッド名に基づいたポインター型が含まれていると思います。したがって、を呼び出すとset.Add(whatever)、元のデータの内部データが変更されます。

于 2012-06-21T03:30:23.043 に答える
3

呼び出し、Goプログラミング言語仕様

関数呼び出しでは、関数値と引数は通常の順序で評価されます。それらが評価された後、呼び出しのパラメーターが値によって関数に渡され、呼び出された関数が実行を開始します。関数の戻りパラメーターは、関数が戻るときに値によって呼び出し元の関数に戻されます。

関数パラメータはいつ値によって渡されますか?FAQ-Goプログラミング言語。

Cファミリのすべての言語と同様に、Goのすべてが値によって渡されます。つまり、関数は、パラメータに値を割り当てる代入ステートメントがあるかのように、渡されるもののコピーを常に取得します。たとえば、int値を関数に渡すと、intのコピーが作成され、ポインター値を渡すと、ポインターのコピーが作成されますが、ポインターが指すデータは作成されません。(これがメソッドレシーバーにどのように影響するかについては、次のセクションを参照してください。)

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

于 2012-06-21T17:28:00.140 に答える