跳到主要内容

Go 语言中的浅拷贝和深拷贝

· 阅读需 3 分钟

什么是浅拷贝?

浅拷贝的意思是,只复制结构体的顶层字段,而不复制它们指向的底层数据。如果结构体中有指针或引用类型的字段,例如切片、映射或通道,那么复制后的变量会共享这些字段的底层数据,而不是创建一个新的副本。

当使用赋值运算符将一个结构体变量复制给另一个结构体变量时,会发生浅拷贝。

func TestShadowCopy(t *testing.T) {
type Car struct {
Brand string
Model string
Owners []string
}

car1 := Car{Brand: "Tesla", Model: "Model X", Owners: []string{"Alice", "Bob"}}
car2 := car1 // Shadow copy

fmt.Printf("car1.Owners 的地址: %p, car2.Owners 的地址: %p\n", &car1.Owners, &car2.Owners)
fmt.Printf("car1.Owners 指向的切片指针: %p, car2.Owners 指向的切片指针: %p\n", car1.Owners, car2.Owners)

// change index 0 of 'Owners' from copy
car2.Owners[0] = "John"

// the Owners from car1 was changed too
fmt.Printf("car1 :%v,\nCar2: %v\n", car1, car2)
}
=== RUN   TestShadowCopy
car1.Owners 的地址: 0xc0000a0ce0, car2.Owners 的地址: 0xc0000a0d20
car1.Owners 指向的切片指针: 0xc000098700, car2.Owners 指向的切片指针: 0xc000098700
car1: {Tesla Model X [John Bob]},
car2: {Tesla Model X [John Bob]}

可以看到,car1 和 car2 是两个不同的结构体变量,所以 car1.Owners 和 car2.Owners 的地址不同,但由于 car1.Owners 是切片类型,赋值的时候发生了浅拷贝,所以 car1.Owners 和 car2.Owners 指向了同一个切片,对 car1.Owners 或 car2.Owners 的修改实际上修改的是同一个切片。

什么是深拷贝?

深拷贝的意思是创建变量的新实例并复制所有内部元素,无论它们在数据结构中的“深度”如何。


func TestDeepCopy(t *testing.T) {
type Car struct {
Brand string
Model string
Owners []string
}
// deepCopyCar performs a deep copy from src to dest.
deepCopyCar := func(src, dest *Car) {
dest.Brand = src.Brand
dest.Model = src.Model

// Create a new slice with its own underlying array
dest.Owners = make([]string, len(src.Owners))

// Copy the content of the original slice to the new slice
copy(dest.Owners, src.Owners)
}

car1 := Car{Brand: "Tesla", Model: "Model X", Owners: []string{"Alice", "Bob"}}
var car2 Car

// Perform the manual deep copy
deepCopyCar(&car1, &car2)

// Modify the first index of the "Owners" slice in car2
car2.Owners[0] = "John"

fmt.Printf("Car1: %v\nCar2: %v\n", car1, car2)
}
=== RUN   TestDeepCopy
car1: {Tesla Model X [Alice Bob]}
car2: {Tesla Model X [John Bob]}

可以看到,通过深拷贝的方式创建的 car2,对 car2 的修改并不会影响到 car1。

何时用浅拷贝?何时用深拷贝?

当不需要对数据进行更改时,用浅拷贝,节省 CPU 时间和内存。

当你需要数据的完整且独立的副本时,用深拷贝。