我想我明白指针是什么,但我不太明白何时使用它
以下代码段来自"A Tour of Go"
" * Vertex"的目的是什么?和"& Vertex"?
我用" Vertex"替换了它们。它运行正常。
package main
import (
"fmt"
"math"
)
type Vertex struct {
X, Y float64
}
func (v *Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func main() {
v := &Vertex{3, 4}
fmt.Println(v.Abs())
}
答案 0 :(得分:5)
这不是指针/值区别的一个特别好的例子,因为在那种情况下它们可以互换!当您需要变异数据时,指针非常有用。#34;远程" (来自另一个职能)。
func (v Vertex) SetX(x int) {
v.X = x
}
func main() {
v := Vertex{3, 4}
fmt.Println(v)
v.SetX(1)
fmt.Println(v)
}
正如你所注意到的,这并没有改变任何东西(严格来说,它改变了顶点的副本,但在大多数情况下它只是语义)! v
的值仍为{3,4}
。尝试改为:
func (v *Vertex) SetX(x int) {
v.X = x
}
func main() {
v := &Vertex{3, 4}
fmt.Println(v)
v.SetX(1)
fmt.Println(v)
}
突然,它有效,第二次打印{1,4}
。现在,如果您有点好奇,可以决定尝试并将v := &Vertex{3, 4}
更改为v := Vertex{3, 4}
。实际上,上面的片段仍然有用。奇怪。同样,如果您更改第二个代码段中的同一行以包含指针,它也会以相同的方式工作。
为什么呢? Go已经"透明"指针。在具有显式指针值(如C或C ++)的其他语言中,您必须显式使用运算符&
和*
来取消引用指针。 C和C ++甚至在字段访问和方法调用v->SetX
上都有特殊的指针追踪语法。
无论好坏,Go都会隐藏此信息。如果你有一个值并且需要调用指针方法,那么Go会很乐意为你做(&v).Method()
,如果你需要取消引用来调用一个值方法,它会很自然地(*v).Method()
。在大多数情况下都是如此,有一些像地图这样的事情并不适用的极端情况,但总的来说这是适用的。
所以,当它归结为它时,你何时应该在方法上使用指针接收器?答案,实际上,大多数情况下是#34;" Go Style Guide通常建议使用指针类型方法接收器,除非接收器是map
,func
或chan
的直接别名,它是一个切片,它不是&{ #39;需要重新设置,或者你对小的不可变数据类型进行优化(因为指针追逐比复制慢一点)。我补充一点,你通常不应该使用直接指针指针。
通常,当您不知道使用哪个时,请使用指针接收器。 99%的时间使用指针将为您提供所期望的行为,特别是如果您习惯使用Python或C#等语言。错误地使用指针导致错误比较罕见,比较获取错误的可能性,因为您的Setter方法实际上没有设置任何东西。
答案 1 :(得分:2)
这个特殊的例子很糟糕,因为在指针类型*Vertex
上定义的方法不会尝试改变其接收者的值(调用该方法的值)。 / p>
在Go中,所有内容都通过值传递/分配 - 包括指针。所以,当你有一个方法
func (v Vertex) Abs() float64 {
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
(注意接收者的类型规范中*
前面没有Vertex
),它可以正常工作,因为当你这样做时
v := Vertex{2, 3}
x := v.Abs()
v
调用网站v.Abs()
的值已复制到Abs()
方法收到的值。
现在假设您想要使用方法调用更改(变更)某些Vertex
变量。一种天真的方法,比如,
func (v Vertex) SetX(x float64) {
v.X = x
}
v := Vertex{2, 3}
v.SetX(-5)
// Here, v.X is still 2
无法正常工作,因为它会更改X
的值v
,而X
已在调用时复制到被调用者;该方法更改了副本的func (v *Vertex) SetX(x float64) {
v.X = x
}
v := Vertex{2, 3}
v.SetX(-5)
- 仅在方法范围内看到的更改。
另一方面,如果你要在指针上定义该方法(它将一个实际值的地址而不是一个值本身),那就可以了:
v
这里,编译器将调用点SetX()
处{{1}}的地址并将其传递给方法。然后,该方法将使用该地址来引用调用者范围中的值。
语法混淆是因为Go(在大多数情况下)允许您不使用运算符来获取值的地址并取消引用该地址。
如果您来自PHP,Python等流行语言之一,那么主要区别在于其中许多对象都是特殊的"并始终通过引用传递。 Go更低级,并且不会在程序员的后面使用魔法,所以你必须明确是否要将指针或值的副本传递给方法。
请注意,这不仅仅是方法是否能够改变其接收者;表演事物也可能在这里起作用。