ara*_*stu 4 methods pointers go
最近读了《Go编程语言书》,这是学习golang编程语言的好资源。\n6.2节中有一段关于类型的复制实例T在方法中是否是指针接收者时类型的复制实例,我无法理解。 \n有没有用一个有意义的例子来解释这一段?
\n\n6.2 使用指针接收器的方法
\n\n如果命名类型 T 的所有方法都有 T 本身的接收者类型(不是 *T ),则复制该类型的实例是安全的;调用它的任何方法都必然会生成一个副本。例如,time.Duration 值可以自由复制,包括作为函数的参数。但是,如果任何方法具有指针接收器,则应避免复制 T 的实例,因为这样做可能会违反内部不变量。例如,复制 bytes.Buffer 的实例将导致原始和副本为相同的底层字节数组别名( \xc2\xa72.3.2 )。后续的方法调用将产生不可预测的效果。
\n\n(Go 编程语言 Alan AA Donovan \xc2\xb7 Brian W. Kernighan)
\n
调用方法时,首先复制调用该方法的值,然后将该副本传递/用作接收者。
\n\n如果一个类型只有带有值接收器的方法,这意味着无论这些方法在内部做什么,也无论你(或其他任何人)调用什么方法,这些方法都无法更改原始值,因为 \xe2 \x80\x93 如上所述,\xe2\x80\x93 只传递了一个副本,并且该方法只能修改副本 \xe2\x80\x93 而不是原始的。
\n\n因此,这意味着如果您复制该值,则不必担心,在原始值或副本上调用的方法都不能/不会修改该值。
\n\n当类型具有带有指针接收器的方法时则不然。如果一个方法有一个指针接收器,则该方法可以更改/修改指向的值,这不是副本,而是原始值(只有指针是副本,但它指向原始值)。
\n\n让我们看一个例子。我们创建一个int包装类型,它有 2 个字段: anint和 an *int。我们打算在两个字段中存储相同的数字,但其中一个是指针(并且我们int将将值中):
type Wrapper struct {\n v int\n p *int\n}\nRun Code Online (Sandbox Code Playgroud)\n\n为了确保两个值 (v和*p) 相同,我们提供了Set()方法来设置这两个值:
func (w *Wrapper) Set(v int) {\n w.v = v\n *w.p = v\n}\nRun Code Online (Sandbox Code Playgroud)\n\nWrapper.Set()有一个指针接收器 ( *Wrapper),因为它必须修改值(类型为Wrapper)。无论我们传递给什么数字Set(),我们都可以确定一旦Set()返回,v和*p都会相同,并且等于传递给的数字Set()。
现在如果我们有一个值为Wrapper:
a := Wrapper{v: 0, p: new(int)}\nRun Code Online (Sandbox Code Playgroud)\n\n我们可以调用Set()它的方法:
a.Set(1)\nRun Code Online (Sandbox Code Playgroud)\n\n编译器会自动将 的地址用作a接收者,所以上面的代码意味着(&a).Set(1)。
我们期望类型的任何值都具有存储在和Wrapper中的相同数字,只要Wrapper.v*Wrapper.pvSet()使用该方法来更改字段的值
现在让我们看看如果我们复制以下问题a:
a := Wrapper{v: 0, p: new(int)}\nb := a\nfmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\\n", a.v, *a.p, b.v, *b.p)\n\na.Set(1)\nfmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\\n", a.v, *a.p, b.v, *b.p)\nRun Code Online (Sandbox Code Playgroud)\n\n输出(在Go Playground上尝试一下):
\n\na.v=0, a.p=0; b.v=0, b.p=0\na.v=1, a.p=1; b.v=0, b.p=1\nRun Code Online (Sandbox Code Playgroud)\n\n我们制作了 的副本a(将其存储在 中b),并打印了值。到目前为止,一切都很好。然后我们调用a.Set(1),之后a仍然很好,但是 的内部状态b变得无效:b.v不再等于*b.p。解释非常清楚:当我们复制a(这是一种struct类型)时,它会复制其字段的值(包括指针p),并且指针 inb将指向与 中的指针相同的值a。因此修改指向的值将影响 的两个副本Wrapper,但我们有 2 个不同的v字段(它们是非指针)。
如果您有带有指针接收器的方法,则应该使用指针值。
\n\n请注意,如果您要复制值*Wrapper,一切仍然很酷:
a := &Wrapper{v: 0, p: new(int)}\nb := a\nfmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\\n", a.v, *a.p, b.v, *b.p)\n\na.Set(1)\nfmt.Printf("a.v=%d, a.p=%d; b.v=%d, b.p=%d\\n", a.v, *a.p, b.v, *b.p)\nRun Code Online (Sandbox Code Playgroud)\n\n输出(在Go Playground上尝试一下):
\n\na.v=0, a.p=0; b.v=0, b.p=0\na.v=1, a.p=1; b.v=1, b.p=1\nRun Code Online (Sandbox Code Playgroud)\n\n在本例中a是一个指针,其类型为*Wrapper。我们制作了它的副本(将其存储在 中b),称为,并且和a.Set()的内部状态仍然有效。这里我们只有一个值,只保存一个指向它的指针(它的地址)。当我们复制时,我们只复制指针值,而不是值(类型abWrapperaastructWrapper)的值。