ps:今天在开发中遇到,实际使用却和我想的并不一样。

不包含指针的指针结构体值传递

type school struct {
	name string
	addr string
}

func main() {
	s1 := &school{
		name: "第一小学",
		addr: "海淀区",
	}

	s2 := new(school)
	*s2 = *s1
	fmt.Printf("s1 = %+v\n", s1)
	fmt.Printf("s2 = %+v\n", s2)

	s2.name = "第二小学"
	s2.addr = "朝阳区"
	fmt.Printf("s1 = %+v\n", s1)
	fmt.Printf("s2 = %+v\n", s2)
}

输出

s1 = &{name:第一小学 addr:海淀区}
s2 = &{name:第一小学 addr:海淀区}
s1 = &{name:第一小学 addr:海淀区}
s2 = &{name:第二小学 addr:朝阳区}

从输出可以明显看出,s2 对象复制了 s1 对象的值,并且两个对象指向不同的内存地址,所以彼此之间修改不影响对方。这只是在不包含指针的情况下的修改,是没有问题的。

包含指针的指针结构体值传递
原本的 school 结构体中添加了 class 结构体指针。

type school struct {
	name string
	addr string
	c    *class
}

type class struct {
	name  string
	class string
}

func main() {
	s1 := &school{
		name: "第一小学",
		addr: "海淀区",
		c: &class{
			name:  "三年级",
			class: "三年级一班",
		},
	}

	s2 := new(school)
	*s2 = *s1
	fmt.Printf("s1 name = %v, addr = %v\n", s1.name, s1.addr)
	fmt.Printf("s2 name = %v, addr = %v\n", s2.name, s2.addr)

	s2.name = "第二小学"
	s2.addr = "通州区"
	fmt.Printf("s1 name = %v, addr = %v\n", s1.name, s1.addr)
	fmt.Printf("s2 name = %v, addr = %v\n", s2.name, s2.addr)

	fmt.Printf("s1 = %+v\n", s1.c)
	fmt.Printf("s2 = %+v\n", s2.c)

	s2.c.name = "四年级"
	s2.c.class = "四年级三班"
	fmt.Printf("s1 = %+v\n", s1.c)
	fmt.Printf("s2 = %+v\n", s2.c)
}

输出

s1 name = 第一小学, addr = 海淀区
s2 name = 第一小学, addr = 海淀区
s1 name = 第一小学, addr = 海淀区
s2 name = 第二小学, addr = 通州区
s1 = &{name:三年级 class:三年级一班}
s2 = &{name:三年级 class:三年级一班}
s1 = &{name:四年级 class:四年级三班}
s2 = &{name:四年级 class:四年级三班}

输出和预想的并不同,先说 class 指针,这时候两个对象指向的是相同的 class 内存地址,所以无论修改 s1 还是 s2 他们彼此的值都会发生改变。

解决办法,直接使用深拷贝复制值,上面的操作属于浅拷贝
更新代码,注意我把结构体中的内容开头全部修改成了大写,不然的话序列化会出现错误。

type School struct {
	Name string
	Addr string
	C    *Class
}

type Class struct {
	Name  string
	Class string
}

func main() {
	s1 := &School{
		Name: "第一小学",
		Addr: "海淀区",
		C: &Class{
			Name:  "三年级",
			Class: "三年级一班",
		},
	}

	s2, err := DeepCopy(s1)
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Printf("S1 class name: %v, class: %v\n", s1.C.Name, s1.C.Class)
	fmt.Printf("S2 class name: %v, class: %v\n", s2.C.Name, s2.C.Class)


	s2.Name = "第二小学"
	s2.Addr = "朝阳区"
	s2.C.Name = "四年级"
	s2.C.Class = "四年级四班"
	fmt.Println(s1)
	fmt.Println(s2)
	fmt.Printf("S1 class name: %v, class: %v\n", s1.C.Name, s1.C.Class)
	fmt.Printf("S2 class name: %v, class: %v\n", s2.C.Name, s2.C.Class)
}

func DeepCopy(s1 *School) (*School, error) {
	if buf, err := json.Marshal(s1); err != nil {
		return nil, err
	} else {
		s2 := &School{}
		if err = json.Unmarshal(buf, s2); err != nil {
			return nil, err
		}
		return s2, nil
	}
}

输出内容

&{第一小学 海淀区 0xc00000c840}
&{第一小学 海淀区 0xc00000c9c0}
S1 class name: 三年级, class: 三年级一班
S2 class name: 三年级, class: 三年级一班
&{第一小学 海淀区 0xc00000c840}
&{第二小学 朝阳区 0xc00000c9c0}
S1 class name: 三年级, class: 三年级一班
S2 class name: 四年级, class: 四年级四班

直接使用 json 来进行序列化反序列化操作,从输出可以看出接收的 s2 对象的 class 的内存地址和 s1 明显不是一个,然后修改 s2 的内容进行打印。毫无意外 s1 没有改成任何更改。
这里是使用 json 来进行序列化反序列化操作,还可以使用 gob 序列化

gob.NewEncoder(buffer).Encode(*object1)
gob.NewDecoder(bytes.NewBuffer(buffer.Bytes())).Decode(object2)

完美,就是感觉有些麻烦。