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

本文探讨了Go语言中深拷贝与浅拷贝的区别,指出值类型默认深拷贝,引用类型如slice默认浅拷贝。通过示例展示了浅拷贝如何共享内存,以及深拷贝的必要性和如何在结构体中实现真正深拷贝,包括无指针情况下的直接赋值和有指针情况下的反射应用。
摘要由CSDN通过智能技术生成

前言

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

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

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

深浅拷贝

本部分参考于此

简介

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

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

Go语言中的深浅拷贝

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

测试代码

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的探索继续思考(其实主要还是那天面试官给的提示…)
所谓深拷贝,其实也只是对第一层的内容进行复制,因此可能导致,复制前后的结构体中,所存的指针依旧指向同一个地址。
见示例代码:

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

结果如下:

在这里插入图片描述

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

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

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

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

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

  • 38
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值