[英]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()
}
题:
m
, treats
和dog
是通过引用传递的 ,并且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
实现Grits
但Whatever
都不实现,因为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
没有。
引用传递是一种语言,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)
,它将更改原始文件的内部数据。
在函数调用中,函数值和参数按通常的顺序求值。 在对它们进行评估之后,调用的参数将按值传递给函数,并且被调用函数开始执行。 当函数返回时,该函数的返回参数按值传递回调用函数。
与C系列中的所有语言一样,Go中的所有内容都按值传递。 也就是说,函数总是获得所传递事物的副本,就像有一个赋值语句将值分配给参数一样。 例如,将int值传递给函数将构成int的副本,而将指针值传递将使指针具有副本,但不会复制其指向的数据。 (有关如何影响方法接收者的讨论,请参见下一节。)
映射和切片值的行为类似于指针:它们是包含指向基础映射或切片数据的指针的描述符。 复制地图或切片值不会复制其指向的数据。 复制接口值将复制存储在接口值中的事物。 如果接口值包含一个结构,则复制接口值将复制该结构。 如果接口值包含一个指针,则复制接口值将复制该指针,但不会复制它指向的数据。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.