繁体   English   中英

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

[英]golang pointers on pointers as function parameters

我有以下功能:

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是一种结构。 因此,我应该传递指针以避免复制数据

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

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

引用传递是一种语言,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) ,它将更改原始文件的内部数据。

调用,Go编程语言规范

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

函数参数何时按值传递? 常见问题解答-Go编程语言。

与C系列中的所有语言一样,Go中的所有内容都按值传递。 也就是说,函数总是获得所传递事物的副本,就像有一个赋值语句将值分配给参数一样。 例如,将int值传递给函数将构成int的副本,而将指针值传递将使指针具有副本,但不会复制其指向的数据。 (有关如何影响方法接收者的讨论,请参见下一节。)

映射和切片值的行为类似于指针:它们是包含指向基础映射或切片数据的指针的描述符。 复制地图或切片值不会复制其指向的数据。 复制接口值将复制存储在接口值中的事物。 如果接口值包含一个结构,则复制接口值将复制该结构。 如果接口值包含一个指针,则复制接口值将复制该指针,但不会复制它指向的数据。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM