结构体浅拷贝和深拷贝
我们在开发中经常到把一个变量复制给另一个变量,那么这个过程,可能是深拷贝。
把一个变量赋值给新的变量的时候,可能涉及到深浅拷贝。
浅拷贝:复制的对象是指针,新老对象指向同一块内存区域,a修改字段,b中字段也跟着变了。内存销毁的时候也是一致的。
深拷贝:拷贝的是数据本身,会新创建一个对象,指向完全不同的内存地址,既然内存地址不同,那么修改也互不影响。
定义和区别
浅拷贝的是数据地址,只复制指向对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化,释放内存地址时,同时释放内存地址。
深拷贝是数据本身,创造一个一样的新的对象,新创建的对象和原对象不共享内存,新创建的对象在内存中开辟了一个新的内存地址,新对象值修改时不会影响原对象值。既然内存地址不同,释放地址时,可以分别释放。
是否真正获取(复制)对象实体,而不是引用
值类型的数据,默认都是深拷贝
int float string bool array struct
引用类型的数据,默认都是浅拷贝
slice map function
结构体深拷贝
结构体中都是值类型的字段 :=赋值就是深拷贝,举例
深拷贝:在结构体当中没有指针,没有切片和映射类型,这样是结构体的深拷贝
package main
import "fmt"
type Per struct {
Name string
Age int
HouseId [3]int
}
func main() {
p1 := Per{
Name: "lucas",
Age: 20,
HouseId: [3]int{1,2,3},
}
p2 := p1
fmt.Printf("%v,%p\n",p1,&p1)
fmt.Printf("%v,%p\n",p2,&p2)
}
{lucas 20 [1 2 3]},0xc00005e180
{lucas 20 [1 2 3]},0xc00005e1b0
可以看到是指向两块内存地址的,说明是深拷贝,修改其中一个属性不合影响另外一个。上面结构体成员都是值类型的,值类型的都是深拷贝。
指针浅拷贝
package main
import "fmt"
type Per struct {
Name string
Age int
HouseId [3]int
}
func main() {
p1 := Per{
Name: "lucas",
Age: 20,
HouseId: [3]int{1,2,3},
}
p2 := &p1
fmt.Printf("%v,%p\n",*p2,p2)
fmt.Printf("%v,%p\n",p1,&p1)
p2.HouseId[0] = 100
fmt.Println(*p2,p1)
}
{lucas 20 [1 2 3]},0xc00006a180
{lucas 20 [1 2 3]},0xc00006a180
{lucas 20 [100 2 3]} {lucas 20 [100 2 3]}
或者使用new
p1 := new(Per)
p1.Name = "lucas"
p1.Age = 20
p1.HouseId = [3]int{1,2,3}
p2 := p1
字段的浅拷贝
package main
import "fmt"
type Per struct {
Name string
Age int
HouseId [3]int
Tags map[string]string
}
func main() {
p1 := Per{
Name: "lucas",
Age: 20,
HouseId: [3]int{1,2,3},
Tags: map[string]string{"k1":"v1","k2":"v2"},
}
p2 := p1
p2.Tags["k1"] = "v10"
fmt.Println(p1,p2)
}
{lucas 20 [1 2 3] map[k1:v10 k2:v2]} {lucas 20 [1 2 3] map[k1:v10 k2:v2]}
map之前三个都是值类型,深拷贝,但是map是引用类型。
结构体当中含有引用类型的字段,那对应的字段是浅拷贝。如果里面有字段会发生浅拷贝,为了避免浅拷贝可以将浅拷贝的字段拿出来单个挨个赋值。
方法1: 挨个可能导致浅拷贝的引用字段自行赋值
可以看到引用类型全部重新赋值了一遍。
package main
import "fmt"
type Per struct {
Name string
Age int
HouseId [3]int
Tags map[string]string
CarId []int
}
func main() {
p1 := Per{
Name: "lucas",
Age: 20,
HouseId: [3]int{1,2,3},
Tags: map[string]string{"k1":"v1","k2":"v2"},
CarId: []int{1,2,3},
}
p2 := p1
tmpCarId := []int{}
for _,v := range p1.CarId{
tmpCarId = append(tmpCarId,v)
}
fmt.Println(tmpCarId)
tmpTags := map[string]string{}
for k,v := range p1.Tags{
tmpTags[k] = v
}
fmt.Println(tmpTags)
p2.Tags = tmpTags
p2.CarId = tmpCarId
}
这样修改就不会互相影响了。
方法2:使用反射或者json
使用json将p1 dump成json,然后再dump回来赋给p2。
data,_ := json.Marshal(p1)
var p3 Per
json.Unmarshal(data,&p3)
这样json dump的时候和内存就没关系了,dump之后再塞回去。
总结:
深拷贝:就是两个完全不同的内存地址,互相修改,互相不影响。