go 爲什麼要有new 和 make
Go 語言就會根據變量的類型自動分配相應的內存。
值類型
對於基礎類型,go語言會根據類型自動分配內存,並賦值這個類型的零值。
如 string 類型的零值是一個空字符串
var a string
fmt.Println(a, &a) // 空串 0xc000010230
引用類型
應用類型本質其實拿到的是一個指針,指針的零值是nil, 所以如果不顯式聲明,是不會自動分配內存的。
var a *string
*a = "test"
fmt.Println(a)
// panic: runtime error: invalid memory address or nil pointer dereference
指針類型沒有分配內存無法使用,強行用會報指針異常錯誤
new()
指針變量默認是沒有分配內存的,那麼需要使用new()函數來分配內存
var a *string
a = new(string)
*a = "test"
fmt.Println(a, &a, *a) // 0xc000010230 0xc00000e028 test
new的作用: 根據傳入的類型申請一塊內存,然後返回指向這塊內存的指針,指針指向的數據就是該類型的零值。
// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type
結構體其實可以當成一些值類型的組合,所以當使用new(struct)後,得到的就是一個指向結構體內存塊的指針。
type person struct {
name string
age int
}
p := new(person)
fmt.Println(p, &p) // &{ 0} 0xc000124018
make()
make 函數創建 map 的時候,其實調用的是 makemap 函數
//src/runtime/map.go
// makemap implements Go map creation for make(map[k]v, hint).
// If the compiler has determined that the map or the first bucket
// can be created on the stack, h and/or bucket may be non-nil.
// If h != nil, the map can be created directly in h.
// If h.buckets != nil, bucket pointed to can be used as the first bucket.
func makemap(t *maptype, hint int, h *hmap) *hmap {
makemap 函數返回的是 *hmap 類型,而 hmap 是一個結構體, 代碼
//src/runtime/map.go
// A header for a Go map.
type hmap struct {
// Note: the format of the hmap is also encoded in cmd/compile/internal/reflectdata/reflect.go.
// Make sure this stays in sync with the compiler's definition.
count int // # live cells == size of map. Must be first (used by len() builtin)
flags uint8
B uint8 // log_2 of # of buckets (can hold up to loadFactor * 2^B items)
noverflow uint16 // approximate number of overflow buckets; see incrnoverflow for details
hash0 uint32 // hash seed
buckets unsafe.Pointer // array of 2^B Buckets. may be nil if count==0.
oldbuckets unsafe.Pointer // previous bucket array of half the size, non-nil only when growing
nevacuate uintptr // progress counter for evacuation (buckets less than this have been evacuated)
extra *mapextra // optional fields
}
這樣的結構體就比較複雜了,需要提前知道map的大小count, 存儲的桶。如果簡單的使用new返回一個*hmap, 並不能很好的使用,這個使用就需要用make函數
m:=make(map[string]int,10)
make函數類似於工廠方法,創建一類相同結構的類型。 如上根據傳遞它的 K-V 鍵值對類型,創建不同類型的 map,同時可以初始化 map 的大小。
總結
- new 函數只用於分配內存,並且把內存清零,也就是返回一個指向對應類型零值的指針
- make 函數只用於 slice、chan 和 map 這三種內置類型的創建和初始化,因爲這三種類型的結構比較複雜,比如 slice 要提前初始化好內部元素的類型,slice 的長度和容量等
所以綜上:
1. new 是爲了解決引用類型,也就是指針剛開始指向一個nil,需要分配內存空間才能使用這個問題設計的
2. make 是爲了解決光new一塊空間後並不能達到目標,還需要添加長度、容量等其他條件才能使用,所以設計了make函數。
每個設計的關鍵字都是爲了解決一類問題的,多多思考有助於我們更好的理解對應的知識。