Go语言中需要注意结构体方法副本传参与指针传参的区别

我们来看个例子:

package main

import (
    "fmt"
)

type B struct {
    Name string
}

func(b B) Test1() {
    fmt.Printf("Test1 addr:%p\n", &b)
    fmt.Printf("Test1 name:%s\n", b.Name)
    b.Name = "john"
}

func(b *B) Test2() {
    fmt.Printf("Test2 addr:%p\n", b)
    fmt.Printf("Test2 name:%s\n", b.Name)
    b.Name = "john"
}

func main() {
    b := B{}
    b.Test1()
    b.Test1()
    b.Test2()
    b.Test2()
}

执行后结果如下:

Test1 addr:0xc42000e1e0
Test1 name:
Test1 addr:0xc42000e1f0
Test1 name:
Test2 addr:0xc42000e1d0
Test2 name:
Test2 addr:0xc42000e1d0
Test2 name:john

可以看到Test1中打印出b结构体的地址在变化,而Test2中没有变化,这说明每一次Test1的调用,都是传入的结构体b的一个副本(拷贝),当在Test1中对内部变量的任何改动,都将会失效(因为下一次访问的时候传入的是b结构体新的副本)。而Test2方法作为指针传参时,每一次传入的都是b结构体的指针,指向的是同一个结构体,因此地址没有变化,且对内部变量做改动时,都是改动的b结构体内容。

在Go语言中的这个差别可能是对OOP设计的一个坑,在Go语言中要想实现OOP的设计,在进行方法封装时,都采用Test2的写法。 Continue reading "Go语言中需要注意结构体方法副本传参与指针传参的区别"