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)
}

结果如下:

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

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

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

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

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

相关推荐
杨荧22 分钟前
【JAVA毕业设计】基于Vue和SpringBoot的宠物咖啡馆平台
java·开发语言·jvm·vue.js·spring boot·spring cloud·开源
monkey_meng35 分钟前
【Rust中的项目管理】
开发语言·rust·源代码管理
喜欢打篮球的普通人37 分钟前
rust高级特征
开发语言·后端·rust
ModelBulider1 小时前
十三、注解配置SpringMVC
java·开发语言·数据库·sql·mysql
V搜xhliang02461 小时前
基于深度学习的地物类型的提取
开发语言·人工智能·python·深度学习·神经网络·学习·conda
DK七七1 小时前
多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码
开发语言·前端·微信小程序·小程序·php
苹果酱05671 小时前
C语言 char 字符串 - C语言零基础入门教程
java·开发语言·spring boot·mysql·中间件
代码小鑫1 小时前
A032-基于Spring Boot的健康医院门诊在线挂号系统
java·开发语言·spring boot·后端·spring·毕业设计
训山2 小时前
4000字浅谈Java网络编程
java·开发语言·网络
API快乐传递者2 小时前
除了网页标题,还能用爬虫抓取哪些信息?
开发语言·爬虫·python