1. 程式人生 > >go new和make的區別

go new和make的區別

Go語言中的內建函式new和make是兩個用於記憶體分配的原語(allocation primitives)。簡單來說,new只分配記憶體,make用於slice,map,和channel的初始化。 

內建函式new按指定型別長度分配零值記憶體,返回指標,並不關心型別內部構造和初始化方式。

內建函式make對引用型別進行建立,編譯器會將make轉換為目標型別專用的建立函式,以確保完成全部記憶體分配和相關屬性初始化

注意的點:

1、new(T) 返回的是 T 的指標

new(T) 為一個 T 型別新值分配空間並將此空間初始化為 T 的零值,返回的是新值的地址,也就是 T 型別的指標 *T,該指標指向 T 的新分配的零值。

2、make 只能用於 slice,map,channel

make 只能用於 slice,map,channel 三種類型,make(T, args) 返回的是初始化之後的 T 型別的值,這個新值並不是 T 型別的零值,也不是指標 *T,是經過初始化之後的 T 的引用

slice切片操作

var s1 []int
if s1 == nil {
    fmt.Printf("s1 is nil --> %#v \n ", s1) // []int(nil)
}
s2 := make([]int, 3)
if s2 == nil {
    fmt.Printf("s2 is nil --> %#v \n ", s2)
} else {
    fmt.Printf("s2 is not nill --> %#v \n ", s2)// []int{0, 0, 0}
}

slice 的零值是 nil,使用 make 之後 slice 是一個初始化的 slice,即 slice 的長度、容量、底層指向的 array 都被 make 完成初始化,此時 slice 內容被型別 int 的零值填充,形式是 [0 0 0],map 和 channel 也是類似的。

3、make(T, args) 返回的是 T 的 引用

如果不特殊宣告,go 的函式預設都是按值穿參,即通過函式傳遞的引數是值的副本,在函式內部對值修改不影響值的本身,但是 make(T, args) 返回的值通過函式傳遞引數之後可以直接修改,即 map,slice,channel 通過函式穿參之後在函式內部修改將影響函式外部的值。

這說明 make(T, args) 返回的是引用型別,在函式內部可以直接更改原始值,對 map 和 channel 也是如此。

4、很少需要使用 new

(1)、struct 初始化的過程,可以說明不使用 new 一樣可以完成 struct 的初始化工作。

//宣告初始化
var foo1 Foo
fmt.Printf("foo1 --> %#v\n ", foo1) //main.Foo{age:0, name:""}
foo1.age = 1
fmt.Println(foo1.age)
//struct literal 初始化
foo2 := Foo{}
fmt.Printf("foo2 --> %#v\n ", foo2) //main.Foo{age:0, name:""}
foo2.age = 2
fmt.Println(foo2.age)
//指標初始化
foo3 := &Foo{}
fmt.Printf("foo3 --> %#v\n ", foo3) //&main.Foo{age:0, name:""}
foo3.age = 3
fmt.Println(foo3.age)
//new 初始化
foo4 := new(Foo)
fmt.Printf("foo4 --> %#v\n ", foo4) //&main.Foo{age:0, name:""}
foo4.age = 4
fmt.Println(foo4.age)
//宣告指標並用 new 初始化
var foo5 *Foo = new(Foo)
fmt.Printf("foo5 --> %#v\n ", foo5) //&main.Foo{age:0, name:""}
foo5.age = 5
fmt.Println(foo5.age)
foo1 和 foo2 是同樣的型別,都是 Foo 型別的值,foo1 是通過 var 宣告,Foo 的 filed 自動初始化為每個型別的零值,foo2 是通過字面量的完成初始化。
foo3,foo4 和 foo5 是一樣的型別,都是 Foo 的指標 *Foo。

但是所有 foo 都可以直接使用 Foo 的 filed,讀取或修改,為什麼?
如果 x 是可定址的,&x 的 filed 集合包含 m,x.m 和 (&x).m 是等同的,go 自動做轉換,也就是 foo1.age 和 foo3.age 呼叫是等價的,go 在下面自動做了轉換。

因而可以直接使用 struct literal 的方式建立物件,能達到和 new 建立是一樣的情況而不需要使用 new。

(2)、new與map的糾紛,new為引用型別分配記憶體,但這是不完整建立。

map 是引用型別的:記憶體用 make 方法來分配。

var map1 = make(map[keytype]valuetype)。
map1 := make(map[keytype]valuetype)。         相當於:mapCreated := map[string]float32{}。
不要使用 new,永遠用 make 來構造 map
注意:如果你錯誤的使用 new() 分配了一個引用物件,你會獲得一個空引用的指標,相當於聲明瞭一個未初始化的變數並且取了它的地址:
mapCreated := new(map[string]float32)

接下來當我們呼叫:mapCreated["key1"] = 4.5 的時候,編譯器會報執行時錯誤。

轉載網址:http://sanyuesha.com/2017/07/26/go-make-and-new/