Golang中的深浅拷贝、结构体的拷贝、或可能的深拷贝失败原因

前言

大一学习C++基础时候便接触过这些概念,转Golang之后便没有再专门学习。直到前些日子的这场面试遇到一个问题------

深浅拷贝 修改拷贝的值,是否影响另一个?

浅拷贝什么时候影响,什么时候不影响?(浅拷贝只是拷贝了第一级,所以根据他内容判断,是值类型还是引用类型)(此部分没找见直接文章,待我回头研究一下源码。。)

再结合自己使用经历来看,的确许多时候,对于一个函数的传参、返回值是否需要带*,还都是有些模糊的。因此专程学习一下相关概念,进行记录。

深浅拷贝

本部分参考于此

简介

深拷贝:创建一个一样的新对象,新分配一块内存。新旧对象的修改操作互不影响。

浅拷贝:只复制指向对象的指针,新旧对象依旧是同一块内存,修改时一起修改。

Go语言中的深浅拷贝

值类型的数据,默认全部都是深复制,Array、Int、String、Struct、Float,Bool。

引用类型的数据,默认全部都是浅复制,Slice,Map。

测试代码

go 复制代码
package main

import (
	"fmt"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	// 浅拷贝
	p1 := Person{Name: "Alice", Age: 25}
	p2 := &p1

	fmt.Printf("浅拷贝 - 修改前:\np1地址:%p\np2地址:%p\n", &p1, &p2)
	fmt.Printf("浅拷贝 - 修改前:\np1内容:%+v\np2内容:%+v\n", p1, p2)

	p1.Name = "修改后"
	fmt.Printf("浅拷贝 - 修改后:\np1地址:%p\np2地址:%p\n", &p1, &p2)
	fmt.Printf("浅拷贝 - 修改后:\np1内容:%+v\np2内容:%+v\n", p1, p2)

	// 深拷贝
	p3 := Person{Name: "Bob", Age: 30}
	p4 := p3
	fmt.Printf("\n深拷贝 - 修改前:\np3地址:%p\np4地址:%p\n", &p3, &p4)
	fmt.Printf("深拷贝 - 修改前:\np3内容:%+v\np4内容:%+v\n", p3, p4)

	p3.Name = "修改后"
	fmt.Printf("深拷贝 - 修改后:\np3地址:%p\np4地址:%p\n", &p3, &p4)
	fmt.Printf("深拷贝 - 修改后:\np3内容:%+v\np4内容:%+v\n", p3, p4)

	// new的情况下,看似是深拷贝其实是浅拷贝
	p5 := new(Person)
	p5.Name = "climber"
	p5.Age = 18
	p6 := p5
	fmt.Printf("\n new的情况下,看似是深拷贝其实是浅拷贝 - 修改前:\np5地址:%p\np6地址:%p\n", &p5, &p6)
	fmt.Printf("深拷贝 - 修改前:\np5内容:%+v\np6内容:%+v\n", p5, p6)

	p3.Name = "修改后"
	fmt.Printf("深拷贝 - 修改后:\np5地址:%p\np6地址:%p\n", &p5, &p6)
	fmt.Printf("深拷贝 - 修改后:\np5内容:%+v\np6内容:%+v\n", p5, p6)

}

测试结果:

浅拷贝:

深拷贝:

特殊情况:使用new

特别注意------使用new时会导致看似深拷贝其实浅拷贝

使用new时候,这里p6 := p5,我们发现二者同时进行了修改,但是地址却不一样。

这是因为new创建时候返回的是指针,所以我们p5存的是一个指向X区域的指针,这个指针在A区。进行p6 := p5时候,相当于对这个A区域的指针进行了复制,复制得到一个在B区的指针(因此打印出来的地址不一样)。但是这个A指针和B指针,都是指向了X区域,所以修改时候会同时修改。

特别注意------结构体的拷贝

顺着上一层new的探索继续思考(其实主要还是那天面试官给的提示...)

所谓深拷贝,其实也只是对第一层的内容进行复制,因此可能导致,复制前后的结构体中,所存的指针依旧指向同一个地址。

见示例代码:

go 复制代码
package main

import "fmt"

type Person struct {
	Name   string
	Age    int
	Scores []int
}

func main() {
	// 创建原始结构体
	original := Person{
		Name:   "Alice",
		Age:    30,
		Scores: []int{90, 85, 80},
	}

	// 对原始结构体进行拷贝
	copy := original

	// 修改拷贝的结构体的参数
	copy.Name = "Bob"
	copy.Age = 25
	copy.Scores[0] = 95

	// 打印原始结构体和拷贝的结构体
	fmt.Println("Original:", original)
	fmt.Println("Copy:", copy)
}

结果如下:

我们发现,切片中的值被一起修改了,符合预期。

结构体如何实现真正的深拷贝?

通过上述测试可以看出,结构体中的地指针都是发生了浅拷贝,其余参数发生的是深拷贝。那么如何才能正确的实现整个结构体的深拷贝呢?

当结构体中,没有指针时,直接赋值就是深拷贝。

当结构体中存在指针和值时,可以通过反射等方法实现,具体见此文

相关推荐
Shartin3 分钟前
CPT208-Human-Centric Computing: Prototype Design Optimization原型设计优化
开发语言·javascript·原型模式
dme.15 分钟前
Javascript之DOM操作
开发语言·javascript·爬虫·python·ecmascript
teeeeeeemo21 分钟前
回调函数 vs Promise vs async/await区别
开发语言·前端·javascript·笔记
加油吧zkf25 分钟前
AI大模型如何重塑软件开发流程?——结合目标检测的深度实践与代码示例
开发语言·图像处理·人工智能·python·yolo
Java技术小馆27 分钟前
GitDiagram如何让你的GitHub项目可视化
java·后端·面试
ac.char32 分钟前
Golang读取ZIP压缩包并显示Gin静态html网站
golang·html·gin
ejinxian40 分钟前
PHP 超文本预处理器 发布 8.5 版本
开发语言·php
星星电灯猴1 小时前
iOS 性能调试全流程:从 Demo 到产品化的小团队实战经验
后端
程序无bug1 小时前
手写Spring框架
java·后端
JohnYan1 小时前
模板+数据的文档生成技术方案设计和实现
javascript·后端·架构