指针上的golang指针作为函数参数

Nic*_*kis 20 pointers go

我有以下功能:

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

//if (complicated thing) add Cat to m

}
Run Code Online (Sandbox Code Playgroud)

其中Set,type的类型treats是具有以下定义的接口:

type Set interface {
  Add(value string)
  Contains(value string) (bool)
  Length() (int)
  RemoveDuplicates()
}
Run Code Online (Sandbox Code Playgroud)

题:

这是真的m,treatsdog通过按引用,并且meowId有它的价值被复制?

我认为:

  • m 是传递参考,因为它是一张地图
  • dog是一个结构.所以,我应该传递指针以避免复制数据

jor*_*lli 38

接口类型只是一组方法.请注意,接口定义的成员不指定接收器类型是否为指针.这是因为值类型的方法集是其关联指针类型的方法集的子集.那是满口的.我的意思是,如果您有以下内容:

type Whatever struct {
    Name string
}
Run Code Online (Sandbox Code Playgroud)

并定义以下两种方法:

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

func (w Whatever) Bar() {
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后类型Whatever只有方法Bar(),而类型*Whatever有方法Foo()Bar().这意味着如果您有以下界面:

type Grits interface {
    Foo()
    Bar()
}
Run Code Online (Sandbox Code Playgroud)

然后*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)
}
Run Code Online (Sandbox Code Playgroud)

你可以调用Raname(&f, "Jorelli Fruit"),但不能Rename(c, "Jorelli Bar"),因为这两个Fruit*Fruit实施Renamable,而*Candy工具RenableCandy没有.

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


Ste*_*erg 6

通过引用传递是一种语言的东西,Go中的任何内容都不是"通过引用传递".通过引用传递意味着赋值操作符可以在单独使用时更改原始值.但是,有一些引用类型,例如指向某处的地图和指针.除非使用其他运算符(如映射索引和*运算符),否则在它们上使用赋值运算符将不会修改原始运算符.

你的地图m是一个引用类型,因此就像一个指针.除替换地图外,对地图的任何更改都将修改原始地图.

m["whatever"] = 2           // Modifies the original map
m = anothermap              // Does not modify the original map
Run Code Online (Sandbox Code Playgroud)

如果存在真正的"按引用传递",则第二个示例将修改原始地图.

传递指针,就像您一样,dog允许您修改原始.如果您调用任何指针方法或使用*运算符,原始将更改.在您的示例中,可能不需要指针.如果Dog很小,可能更容易传递副本.由程序员决定何时使用指针是一个好时机.

Set未通过引用传递.接口不是引用.虽然在6g编译器内部接口使用指针是正确的,但接口本身并不像一个接口.传递接口,无论它包含的对象的大小,都与使用6g编译器传递指针一样便宜.但是,无法使用指针和贴图修改接口的原始值.

虽然您无法修改传递的原始接口,但该接口可以包含指针类型.在这种情况下,它将像狗指针一样,调用某些方法可以修改原始.对于您的特定Set接口,我猜它包含基于方法名称的指针类型.因此,当您致电时set.Add(whatever),它将更改原始内部数据.