go 爲什麼要有new 和 make

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 的大小。

總結

  1. new 函數只用於分配內存,並且把內存清零,也就是返回一個指向對應類型零值的指針
  2. make 函數只用於 slice、chan 和 map 這三種內置類型的創建和初始化,因爲這三種類型的結構比較複雜,比如 slice 要提前初始化好內部元素的類型,slice 的長度和容量等

所以綜上:

1. new 是爲了解決引用類型,也就是指針剛開始指向一個nil,需要分配內存空間才能使用這個問題設計的

2. make 是爲了解決光new一塊空間後並不能達到目標,還需要添加長度、容量等其他條件才能使用,所以設計了make函數。

每個設計的關鍵字都是爲了解決一類問題的,多多思考有助於我們更好的理解對應的知識。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章