经过9年的C ++开发,我正在探索Go。在C ++中,由于性能损失,除了内置类型的变量之外,按值传递函数的参数是一种不好的做法:参数的所有字段都将被复制,在大多数情况下,这将是一个非常昂贵的操作。
这对Go来说是真的吗?通过值传递“this”仅仅为方法分配“const”语义看起来非常昂贵。 Go编译器是否足够智能以防止在第一次修改之前复制变量?为什么不通过值将“this”传递给Go中的反模式,就像在C / C ++中一样?
答案 0 :(得分:8)
其他答案都很好,但在我看来,缺少一些信息。
Go中的接收者只是语法糖,如以下code所示:
package main
import "fmt"
type Something struct {
Value int
}
func (s *Something) ChangeValue(n int) {
s.Value = n
}
func main() {
o := new(Something) // o is of type *Something
fmt.Println(o.Value) // Prints 0
o.ChangeValue(8) // Changes o.Value to 8
fmt.Println(o.Value) // Prints 8
(*Something).ChangeValue(o, 16) // Same as calling o.ChangeValue(16)
fmt.Println(o.Value) // Prints 16
}
基于此,考虑如果ChangeValue
的接收者是Something
类型的值而不是指向一个的指针会发生什么......
没错!你永远不会通过这种方法实际改变o
的{{1}}字段。大多数情况下,您使用指针接收器进行封装。
答案 1 :(得分:6)
也就是说,这就是我不鼓励编写接收器变量名为this
或self
的方法的原因。对于习惯其他语言的人来说,这会产生误导。
一个完全构成的例子,充满希望地说明了这个想法:
func (n *node) walk(f func(*node)) {
for n != nil {
f(n)
n = n.next
}
}
答案 2 :(得分:5)
我想说你的C ++知识将很好地转化为Go作为函数参数(通过值传递结构)和不存在的内容(内置类型,例如int)。
主要区别在于引用类型,切片,map
和channel
。虽然它们似乎是通过值传递的(你不需要使用指针)实际上是通过引用传递的,所以一般不要使用指向切片,地图或通道的指针。
string
也很特别 - 它们是引擎类型下的参考类型,但它们也是不可变的,所以直接传递它们。
至于this
或Go中调用的接收器的具体情况 - 同样的规则适用(注意你可以将内置类型作为接收器而不像C ++),我不认为编译器足够智能,以避免副本,所以使用指针大结构。
答案 3 :(得分:5)
这取决于接收器的大小。如果接收器少于几十个字节,复制它实际上可能比指针追逐(额外的存储器访问)便宜,如果你传递一个指针就需要。此外,使用指针使得更有可能在堆上分配结构,这给垃圾收集器带来了额外的负担。
在Go中,副本始终是逐字节副本,因此成本仅取决于结构的大小。在C ++中,它可能会调用复制构造函数,这可能会占用大量时间。
因此,除了非常大的对象之外,根据方法的语义和与API其余部分的一致性,使用任何类型的接收器最有意义。