Golang 结构体struct 深浅拷贝

结构体浅拷贝和深拷贝


我们在开发中经常到把一个变量复制给另一个变量,那么这个过程,可能是深拷贝。把一个变量赋值给新的变量的时候,可能涉及到深浅拷贝。

浅拷贝:复制的对象是指针,新老对象指向同一块内存区域,a修改字段,b中字段也跟着变了。内存销毁的时候也是一致的。

深拷贝:拷贝的是数据本身,会新创建一个对象,指向完全不同的内存地址,既然内存地址不同,那么修改也互不影响。

定义和区别


浅拷贝的是数据地址,只复制指向对象的指针,此时新对象和老对象指向的内存地址是一样的,新对象值修改时老对象也会变化,释放内存地址时,同时释放内存地址。

深拷贝是数据本身,创造一个一样的新的对象,新创建的对象和原对象不共享内存,新创建的对象在内存中开辟了一个新的内存地址,新对象值修改时不会影响原对象值。既然内存地址不同,释放地址时,可以分别释放。

是否真正获取(复制)对象实体,而不是引用

值类型的数据,默认都是深拷贝

int、float、string、bool、array、struct

引用类型的数据,默认都是浅拷贝

slice、map、function

结构体深拷贝


结构体中都是值类型的字段 := 赋值就是深拷贝,举例:

深拷贝:在结构体当中没有指针,没有切片和映射类型,这样是结构体的深拷贝

type Per struct {
  Name string
  Age int
  HouseId [3]int
}

func main()  {
  p1 := Per{
	  Name:    "lucas",
	  Age:     20,
	  HouseId: [3]int{1,2,3},
  }

  p2 := p1

  fmt.Printf("%v,%p\n",p1,&p1)
  fmt.Printf("%v,%p\n",p2,&p2)
}

{lucas 20 [1 2 3]},0xc00005e180
{lucas 20 [1 2 3]},0xc00005e1b0

可以看到是指向两块内存地址的,说明是深拷贝,修改其中一个属性不合影响另外一个。上面结构体成员都是值类型的,值类型的都是深拷贝。

指针浅拷贝


package main

import "fmt"

type Per struct {
  Name string
  Age int
  HouseId [3]int
}

func main()  {
  p1 := Per{
	  Name:    "lucas",
	  Age:     20,
	  HouseId: [3]int{1,2,3},
  }

  p2 := &p1

  fmt.Printf("%v,%p\n",*p2,p2)
  fmt.Printf("%v,%p\n",p1,&p1)

  p2.HouseId[0] = 100
  fmt.Println(*p2,p1)


}


{lucas 20 [1 2 3]},0xc00006a180
{lucas 20 [1 2 3]},0xc00006a180
{lucas 20 [100 2 3]} {lucas 20 [100 2 3]}

或者使用new

	p1 := new(Per)
	p1.Name = "lucas"
	p1.Age = 20
	p1.HouseId = [3]int{1,2,3}

	p2 := p1

字段的浅拷贝


package main

import "fmt"

type Per struct {
  Name string
  Age int
  HouseId [3]int
  Tags map[string]string
}

func main()  {

p1 := Per{
	Name:    "lucas",
	Age:     20,
	HouseId: [3]int{1,2,3},
	Tags: map[string]string{"k1":"v1","k2":"v2"},
}

p2 := p1

p2.Tags["k1"] = "v10"
fmt.Println(p1,p2)
}


{lucas 20 [1 2 3] map[k1:v10 k2:v2]} {lucas 20 [1 2 3] map[k1:v10 k2:v2]}

map之前三个都是值类型,深拷贝,但是map是引用类型,浅拷贝。

结构体当中含有引用类型的字段,那对应的字段是浅拷贝。如果里面有字段会发生浅拷贝,为了避免浅拷贝可以将浅拷贝的字段拿出来单个挨个赋值。

方法1: 浅拷贝的引用字段重新自行赋值

其实就是将引用指向另外一个地方 

可以看到引用类型全部重新赋值了一遍。 

package main

import "fmt"

type Per struct {
  Name string
  Age int
  HouseId [3]int
  Tags map[string]string
  CarId []int
}

func main()  {

p1 := Per{
	Name:    "lucas",
	Age:     20,
	HouseId: [3]int{1,2,3},
	Tags: map[string]string{"k1":"v1","k2":"v2"},
	CarId: []int{1,2,3},
}

p2 := p1

tmpCarId := []int{}

for _,v := range p1.CarId{
	tmpCarId = append(tmpCarId,v)
}
fmt.Println(tmpCarId)


    tmpTags := map[string]string{}
	for k,v := range p1.Tags{
	 tmpTags[k] = v
	}
	fmt.Println(tmpTags)
	
	p2.Tags = tmpTags
	p2.CarId = tmpCarId

}

这样修改就不会互相影响了。

方法2:使用反射或者json

使用json将p1 dump成json,然后再dump回来赋给p2。 这样json dump的时候和内存就没关系了,dump之后再塞回去。

	data,_ := json.Marshal(p1)
	var p3 Per
	json.Unmarshal(data,&p3)



type Man struct {
	Name    string            `json:"name"`
	HouseId []int             `json:"houseId"`
	Tags    map[string]string `json:"tags"`
}

func main() {
	m1 := Man{
		Name:    "jordon",
		HouseId: []int{1, 2, 3, 4},
		Tags:    map[string]string{"k1": "v1", "k2": "v2"},
	}

	var m3 Man
	data, _ := json.Marshal(&m1)
	fmt.Println(string(data))
	json.Unmarshal(data, &m3)
	fmt.Printf("%v", m3)
}

{"name":"jordon","houseId":[1,2,3,4],"tags":{"k1":"v1","k2":"v2"}}
{jordon [1 2 3 4] map[k1:v1 k2:v2]} 

总结 


深拷贝:就是两个完全不同的内存地址,互相修改,互相不影响。

浅拷贝:指向的是同一块内存地址,修改互相影响。

深拷贝的使用:=去赋值就行了,但是前提是结构体全部都是值类型的成员。如果想浅拷贝,第一种使用指针,第二种使用new函数。

如果想要达到深拷贝,成员里面含有引用类型变量。第一种,字段挨个赋值。第二种使用json,dump出来再dump回去,中间做了一次转化,效率低一些。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Go语言中,结构体的初始化可以通过以下几种方法来完成。首先,可以使用键值对的方式来初始化结构体的成员变量。例如,定义一个名为cat的结构体,包含name、age和sex三个成员变量,可以通过以下方式进行初始化: cat1 := cat{ name: "小白", age: 12, sex: "male", } 另一种初始化结构体的方法是使用多个值的列表来初始化结构体的成员变量。例如,可以按照结构体定义的顺序依次传入相应的值进行初始化: cat1 := cat{"小白", 12, "male"} 还可以使用指针类型的结构体进行初始化。例如,定义一个指向cat结构体的指针类型变量,可以通过以下方式进行初始化: cat1 := &cat{ name: "小白", age: 12, sex: "male", } 最后,还可以使用匿名结构体进行初始化。匿名结构体没有定义结构体类型名,直接使用花括号{}进行初始化。例如: cat1 := struct { name string age int sex string }{ name: "小白", age: 12, sex: "male", } 这些是几种常见的初始化结构体的方法,根据实际情况选择适合的方式进行初始化即可。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [Go语言结构体如何进行初始化](https://blog.csdn.net/weixin_52723461/article/details/124819621)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Golang结构体和map](https://download.csdn.net/download/weixin_38731199/14037169)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值