一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

在Go语言中通过语言学习理解深拷贝与浅拷贝的差异

时间:2026-07-20 17:25:54 编辑:袖梨 来源:一聚教程网

Go语言无深拷贝特性,赋值均为值传递;struct浅拷贝逐字段复制:值类型完全独立,引用类型共享底层数据;slice需make+copy实现真隔离;反射或序列化深拷贝有局限,手动按语义逐字段处理最可靠。

Go 语言里没有“深拷贝”这个语言特性,所有赋值、传参、返回都是值传递;所谓深/浅,只取决于你操作的是什么类型、它底层是否含引用字段。

struct 赋值默认是浅拷贝,但不是所有字段都共享

copy := original 时,Go 会逐字段复制:

  • 值类型字段(intstring、不含指针的嵌套 struct):内存内容完整复制,改 copy.Age 不影响 original.Age
  • 引用类型字段([]Tmap[K]V*Tchan T):只复制头信息(如 slice 的 len/cap/底层数组指针),底层数据仍共用

例如 original.Tagscopy.Tags 指向同一底层数组,改 copy.Tags[0] 就等于改 original.Tags[0]original.Infocopy.Info 若为 *Detail,则两者解引用后操作的是同一块内存。

slice 拷贝必须用 copy() 才算值拷贝

b := ab := a[:] 都是浅拷贝——两个 slice 共享底层数组。要真正隔离,得手动分配新底层数组:

立即学习“go语言免费学习笔记(深入)”;

  • make([]T, len(a)) 分配空间
  • copy(b, a) 复制元素值
  • T 本身含引用字段(如 []map[string]int),copy() 只复制外层 slice,内部 map 仍共享,需额外递归处理

不这么做,哪怕只改 b[0].Namea[0].Name 也会变。

用反射或序列化实现深拷贝时,要注意语义丢失

第三方库如 deepcopy 或手写反射递归,能自动处理嵌套 mapslicestruct,但有硬伤:

  • 未导出字段(小写开头)无法被反射访问,直接跳过
  • funcchanunsafe.Pointer 无法安全复制,通常 panic 或忽略
  • JSON 序列化方案(json.Marshal + json.Unmarshal)会丢掉方法、空接口类型、循环引用,且要求字段可导出

这些不是“实现不到位”,而是 Go 类型系统本身不支持通用深拷贝——你得自己决定哪些字段该深、哪些该共享、哪些该忽略。

手动深拷贝的关键是明确字段意图

比起依赖通用方案,更可靠的是按业务语义逐字段写拷贝逻辑:

  • []T:用 make + copy,若 T 是结构体且含指针,对每个 T 再调用其自有拷贝方法
  • map[K]Vmake(map[K]V) 后遍历赋值,若 V 是引用类型,也需递归深拷贝
  • *T:判空后 dst = &(*src),若 T 有引用字段,继续展开

最易被忽略的点是:深拷贝不是技术问题,而是设计问题——你要拷什么、不拷什么、怎么处理 nil、如何应对自引用,这些都没法靠一个函数自动推断。

热门栏目