zoukankan      html  css  js  c++  java
  • Golang结构体类型的深浅拷贝

    在开发中会经常的把一个变量复制给另一个变量,有时候为了不让对象直接相互干扰,我们需要深度赋值对象

    那么这个过程,可能涉及到深浅拷贝

    1、浅拷贝

    浅拷贝是指对地址的拷贝

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

    引用类型的都是浅拷贝:slicemapfunction

    浅拷贝的特点:

    • 拷贝的时候仅仅拷贝地址,地址指向的都是同一个值

    • a中修改,则b中也跟着变化

    • 内存销毁是一致的

    2、深拷贝

    深拷贝是指将地址指向的值进行拷贝

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

    值类似的都是深拷贝:intfloatboolarraystruct

    深拷贝的特点:

    • 复制的时候会新创建一个对象
    • 指向完全不同的内存地址
    • 修改是互不影响的

    通过指针求值,将值拷贝实现,修改拷贝的值不影响原来的值

    type Author struct {
    	Name string
    	Aage int
    }
    
    type Title struct {
    	Main string
    	Sub  string
    }
    
    type Book2 struct {
    	Author *Author
    	Title  *Title
    }
    
    func (b *Book2) GetName() string {
    	return b.Author.GetName() + "book"
    }
    
    func TestMain8(t *testing.T) {
    	b1 := Book2{
    		Author: &Author{
    			Name: "old author",
    		},
    		Title: &Title{},
    	}
    
    	b2 := &Book2{
    		Author: &Author{},
    		Title: &Title{},
    	}
    	*b2.Author = *b1.Author
    	*b2.Title = *b1.Title
    
    	b2.Author.Name = "new author"
    	fmt.Println(b1.Author.Name)  // old author
    	fmt.Println(b2.Author.Name)  // new author
    }
    

    3、结构体的深拷贝

    默认情况下,结构体类型中的字段是值类型,拷贝时都是深拷贝

    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int
    }
    
    func main()  {
    	p1 := Per{
    		Name:     "ssgeek",
    		Age:      24,
    		HouseIds: [2]int{22, 33},
    	}
    	p2 := p1
    	fmt.Printf("%v %p 
    ", p1, &p1)  // {ssgeek 24 [22 33]} 0xc000180030
    	fmt.Printf("%v %p 
    ", p2, &p2)  // {ssgeek 24 [22 33]} 0xc000180060
    	p2.Age = 19
    	p2.Name = "likui"
    	p2.HouseIds[1] = 44
    	fmt.Printf("%v %p 
    ", p1, &p1)  // {ssgeek 24 [22 33]} 0xc000098180
    	fmt.Printf("%v %p 
    ", p2, &p2)  // {likui 19 [22 44]} 0xc0000981b0
    }
    

    4、结构体的浅拷贝

    使用指针进行浅拷贝,浅拷贝中,可以看到p1p2的内存地址是相同的,修改其中一个对象的属性时,另一个也会产生变化

    package main
    
    import "fmt"
    
    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int
    }
    
    func main()  {
    	p1 := Per{
    		Name:     "ssgeek",
    		Age:      24,
    		HouseIds: [2]int{22, 33},
    	}
    	p2 := &p1
    	fmt.Printf("%v %p 
    ", p1, &p1)  // {ssgeek 24 [22 33]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, p2)  // &{ssgeek 24 [22 33]} 0xc000076180
    	p2.Age = 19
    	p2.Name = "likui"
    	p2.HouseIds[1] = 44
    	fmt.Printf("%v %p 
    ", p1, &p1)  // {likui 19 [22 44]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, p2)  // &{likui 19 [22 44]} 0xc000076180
    }
    

    5、结构体值类型的浅拷贝

    使用new函数实现值类型的浅拷贝

    值类型的默认是深拷贝,想要实现值类型的浅拷贝,一般是两种方法

    • 使用指针
    • 使用new函数(new函数返回的是指针)
    package main
    
    import "fmt"
    
    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int
    }
    
    func main()  {
    	p1 := new(Per)
    	p1.HouseIds = [2]int{22, 33}
    	p1.Name = "songjiang"
    	p1.Age = 20
    
    	p2 := p1
    	fmt.Printf("%v %p 
    ", p1, p1)  // &{songjiang 20 [22 33]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, p2)  // &{songjiang 20 [22 33]} 0xc000076180
    	p2.Age = 19
    	p2.Name = "likui"
    	p2.HouseIds[1] = 44
    	fmt.Printf("%v %p 
    ", p1, p1)  // &{likui 19 [22 44]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, p2)  // &{likui 19 [22 44]} 0xc000076180
    }
    

    6、结构体引用类型的浅拷贝

    结构体默认是深拷贝,但如果结构体中包含map、slice等这些引用类型,默认也还是浅拷贝

    map是引用类型,引用类型浅拷贝是默认的情况

    package main
    
    import "fmt"
    
    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int  // 数组,指定了长度
    	CarIds   []int  // 切片,没指定长度
    	Labels   map[string]string
    }
    
    func main() {
    	//p1 := new(Per)
    	p1 := Per{
    		Name:     "ssgeek",
    		Age:      24,
    		HouseIds: [2]int{22, 33},  // 数组,指定了长度,深拷贝
    		CarIds: []int{911, 718},  // 切片,引用类型,浅拷贝
    		Labels:   map[string]string{"k1": "v1", "k2": "v2"},
    		// 上述三个都是值类型,深拷贝,这个map是引用类型,浅拷贝
    	}
    	p2 := p1
    	fmt.Printf("%v %p 
    ", p1, &p1) // {ssgeek 18 [22 33] map[k1:v1 k2:v2]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, &p2) // {ssgeek 18 [22 33] map[k1:v1 k2:v2]} 0xc0000761e0
    	p2.Age = 19
    	p2.Name = "likui"
    	p2.HouseIds[1] = 44
    	p2.CarIds[0] = 119
    	p2.Labels["k1"] = "m1"
    	fmt.Printf("%v %p 
    ", p1, &p1) // {ssgeek 24 [22 33] map[k1:m1 k2:v2]} 0xc000076180
    	fmt.Printf("%v %p 
    ", p2, &p2) // {likui 19 [22 44] map[k1:m1 k2:v2]} 0xc0000761e0
    }
    
    

    7、结构体引用类型的深拷贝

    结构体中含有引用类型的字段,那么这个字段就是浅拷贝,但是往往希望的是深拷贝,解决方案如下

    • 方法一:挨个把可导致浅拷贝的引用类型字段自行赋值

    赋值后,修改值就相互不影响了

    package main
    
    import "fmt"
    
    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int  // 数组,指定了长度
    	CarIds   []int  // 切片,没指定长度
    	Labels   map[string]string
    }
    
    func main() {
    	//p1 := new(Per)
    	p1 := Per{
    		Name:     "ssgeek",
    		Age:      24,
    		HouseIds: [2]int{22, 33},  // 数组,指定了长度,深拷贝
    		CarIds: []int{911, 718},  // 切片,引用类型,浅拷贝
    		Labels:   map[string]string{"k1": "v1", "k2": "v2"},
    		// 上述三个都是值类型,深拷贝,这个map是引用类型,浅拷贝
    	}
    	p2 := p1
    	// 切片赋值到新的切片
    	tmpCarIds := make([]int, 0)
    	for _, c := range p1.CarIds{
    		tmpCarIds = append(tmpCarIds, c)
    	}
    	// map赋值到新的map
    	tmpLabels := make(map[string]string)
    	for k, v := range p1.Labels{
    		tmpLabels[k] = v
    	}
    	p2.CarIds = tmpCarIds
    	p2.Labels = tmpLabels
    	fmt.Printf("%v %p 
    ", p1, &p1) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c050
    	fmt.Printf("%v %p 
    ", p2, &p2) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c0a0
    	p1.Age = 19
    	p1.Name = "likui"
    	p1.HouseIds[1] = 44
    	p1.CarIds[0] = 119
    	p1.Labels["k1"] = "m1"
    	fmt.Printf("%v %p 
    ", p1, &p1) // {likui 19 [22 44] [119 718] map[k1:m1 k2:v2]} 0xc00006c050
    	fmt.Printf("%v %p 
    ", p2, &p2) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c0a0
    }
    
    • 方法二:使用json或反射

    简单来说:json将引用类型的数据进行dumpdump后就和原来的引用类型没有关系了

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Per struct {
    	Name     string
    	Age      int
    	HouseIds [2]int // 数组,指定了长度
    	CarIds   []int  // 切片,没指定长度
    	Labels   map[string]string
    }
    
    func main() {
    	p1 := Per{
    		Name:     "ssgeek",
    		Age:      24,
    		HouseIds: [2]int{22, 33},  // 数组,指定了长度,深拷贝
    		CarIds:   []int{911, 718}, // 切片,引用类型,浅拷贝
    		Labels:   map[string]string{"k1": "v1", "k2": "v2"},
    		// 上述三个都是值类型,深拷贝,这个map是引用类型,浅拷贝
    	}
    	data, _ := json.Marshal(p1)
    	var p2 Per
    	json.Unmarshal(data, &p2)
    	fmt.Printf("%v %p 
    ", p1, &p1) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c050
    	fmt.Printf("%v %p 
    ", p2, &p2) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c140
    	p1.Age = 19
    	p1.Name = "likui"
    	p1.HouseIds[1] = 44
    	p1.CarIds[0] = 119
    	p1.Labels["k1"] = "m1"
    	fmt.Printf("%v %p 
    ", p1, &p1) // {likui 19 [22 44] [119 718] map[k1:m1 k2:v2]} 0xc00006c050
    	fmt.Printf("%v %p 
    ", p2, &p2) // {ssgeek 24 [22 33] [911 718] map[k1:v1 k2:v2]} 0xc00006c140
    }
    
    • 方法三:使用其他三方库(这里还没深入)

    See you ~

  • 相关阅读:
    php解决前端调接口跨域问题
    降低token 被盗风险安全问题
    u盘怎么解除写保护状态,u盘写保护怎么去掉
    安装vsftpd时出现错误
    对于vim 编译器的设置
    Vim 怎么设置显示行号,永久性显示行号
    Ubuntu系统设置过程中信息显示不全
    控制文件夹名显示的长短
    linux中好玩的游戏
    安装VMware Tools
  • 原文地址:https://www.cnblogs.com/ssgeek/p/15047079.html
Copyright © 2011-2022 走看看