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)
完美,就是感觉有些麻烦。