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

时间:2012-06-21 01:59:32

标签: pointers go

我有以下功能:

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

问题:

mtreatsdog 传递参考是否属实,而meowId是否复制了它的值?

我认为:

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

3 个答案:

答案 0 :(得分:38)

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

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会实施Renable }和Candy没有。

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

答案 1 :(得分: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)时,它会更改原始内部数据。

答案 2 :(得分:3)

  

Calls, The Go Programming Language Specification

     

在函数调用中,将计算函数值和参数   通常的顺序。在评估它们之后,调用的参数   通过值传递给函数,并且被调用的函数开始   执行。函数的返回参数按值传递   当函数返回时返回调用函数。

     

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

     

与C系列中的所有语言一样,   Go中的所有内容都按值传递。也就是说,一个函数总是得到一个   被传递的东西的副本,好像有一个任务   语句将值赋给参数。例如,传球   函数的int值生成int的副本,并传递一个   指针值生成指针的副本,但不指向它指向的数据   至。 (有关如何影响方法的讨论,请参阅下一节   接收机。)

     

Map和slice值的行为类似于指针:它们是描述符   包含指向底层地图或切片数据的指针。复制地图或   切片值不会复制它指向的数据。复制界面   value生成存储在接口值中的东西的副本。如果   接口值包含一个struct,复制接口值使得一个   结构的副本。如果接口值包含指针,则复制   接口值制作指针的副本,但同样不是   它指向的数据。