深入學習golang(4)—new與make
Go語言中的內建函式new和make是兩個用於記憶體分配的原語(allocation primitives)。對於初學者,這兩者的區別也挺容易讓人迷糊的。簡單的說,new只分配記憶體,make用於slice,map,和channel的初始化。
1. new
這是一個用來分配記憶體的內建函式,但是與C++不一樣的是,它並不初始化記憶體,只是將其置零。也就是說,new(T)會為T型別的新專案,分配被置零的儲存,並且返回它的地址,一個型別為*T的值。在Go的術語中,其返回一個指向新分配的型別為T的指標,這個指標指向的內容的值為零(zero value)。注意並不是指標為零。
Go語言中的物件沒有C++中的建構函式,如果用C來描述,Go中的new大概相當於:
T *t = (T*)malloc(sizeof(T))
memset(t, 0, sizeof(T))
其實,上面的描可能也不是很準確,也許用*t=zerovalue更準確。因為對於不同的資料型別,零值的意義是完全不一樣的。比如,對於bool型別,零值為false;int的零值為0;string的零值是空字串:
b := new(bool)
fmt.Println(*b)
i := new(int)
fmt.Println(*i)
s := new(string)
fmt.Println(*s)
輸出:
false
0
注意最後有一個空字串。
2. 初始化
很多時候,零值並不是一個好主意,我們需要做一些初始化。考慮如下結構體:
type Rect struct {
x, y float64
width, height float64
}
零值的Rect並沒有多大用處,我們以下方式進行初始化:
rect3 := &Rect{0, 0, 100, 200}
rect4 := &Rect{width: 100, height: 200}
再申明一下,Go語言中沒有C++中的建構函式,物件的建立一般交給一個全域性的建立函式來完成:
func NewRect(x, y, width, height float64) *Rect {
return &Rect{x, y, width, height}
}
注意,這裡與C/C++不同的是,返回一個區域性變數的地址在Go語言中是絕對沒有問題的;變數關聯的儲存在函式返回之後依然存在。
3. make
內建函式make(T, args)與new(T)的用途不一樣。它只用來建立slice,map和channel,並且返回一個初始化的(而不是置零),型別為T的值(而不是*T)。之所以有所不同,是因為這三個型別的背後引用了使用前必須初始化的資料結構。例如,slice是一個三元描述符,包含一個指向資料(在陣列中)的指標,長度,以及容量,在這些項被初始化之前,slice都是nil的。對於slice,map和channel,make初始化這些內部資料結構,並準備好可用的值。
例如,
make([]int, 10, 100)
分配一個有100個int的陣列,然後建立一個長度為10,容量為100的slice結構,該slice引用包含前10個元素的陣列。對應的,new([]int)返回一個指向新分配的,被置零的slice結構體的指標,即指向值為nil的slice的指標。
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
// Unnecessarily complex:這種做法實在是很蛋疼
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// Idiomatic:習慣的做法
v := make([]int, 100)
記住make只用於map,slice和channel,並且不返回指標。要獲得一個顯式的指標,使用new進行分配,或者顯式地使用一個變數的地址。
主要參考
《effective go》
作者:YY哥
出處:http://www.cnblogs.com/hustcat/
本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段宣告,且在文章頁面明顯位置給出原文連線,否則保留追究法律責任的權利。