Go 学习笔记(30)— Go 语言 make 和 new 的区别
Go 語言中 new 和 make 是兩個(gè)內(nèi)置函數(shù),主要用來創(chuàng)建并分配類型的內(nèi)存。在我們定義變量的時(shí)候,可能會(huì)覺得有點(diǎn)迷惑,不知道應(yīng)該使用哪個(gè)函數(shù)來聲明變量,其實(shí)他們的規(guī)則很簡單:
new只分配內(nèi)存make只能用于slice、map和channel的初始化
1. new
在 Go 語言中, new 函數(shù)描述如下:
// 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 函數(shù)只接受一個(gè)參數(shù),這個(gè)參數(shù)是一個(gè)類型,并且返回一個(gè)指向該類型內(nèi)存地址的指針。同時(shí) new 函數(shù)會(huì)把分配的內(nèi)存置為零,也就是類型的零值。
new() 函數(shù)可以創(chuàng)建一個(gè)對應(yīng)類型的指針,創(chuàng)建過程會(huì)分配內(nèi)存,被創(chuàng)建的指針指向默認(rèn)值。
注意, Go 中的 new 關(guān)鍵字并不是聲明變量,而是返回該類型的指針
a := new(int) //這時(shí)候 a 是一個(gè)*int 指針變量
使用 new 函數(shù)為變量分配內(nèi)存空間。
var sum *int
sum = new(int) //分配空間
*sum = 98
fmt.Println(*sum)
當(dāng)然, new 函數(shù)不僅僅能夠?yàn)橄到y(tǒng)默認(rèn)的數(shù)據(jù)類型,分配空間,自定義類型也可以使用 new 函數(shù)來分配空間,如下所示:
type Student struct {name stringage int
}var s *Student
s = new(Student) //分配空間
s.name ="dequan"fmt.Println(s)
這里如果我們不使用 new 函數(shù)為自定義類型分配空間(將第 7 行注釋),就會(huì)報(bào)錯(cuò):
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x80bd277]
goroutine 1 [running]:
這就是 new 函數(shù),它返回的永遠(yuǎn)是類型的指針,指針指向分配類型的內(nèi)存地址。
使用示例:
package main// 導(dǎo)入系統(tǒng)包
import ("fmt"
)func main() {str := new(string)*str = "hello,world"value := new(int)*value = 10fmt.Println(*str) // hello,worldfmt.Println(*value) // 10}
new 函數(shù)用來創(chuàng)建某一個(gè)類型的指針型對象。理論上,一個(gè)數(shù)據(jù)類型,只要能夠被訪問,就可以使用 new 函數(shù)來創(chuàng)建這個(gè)類型的指針型對象。下邊來看一段示例,使用 new 函數(shù)創(chuàng)建基本類型的指針型對象。
package main
import ("fmt"
)
type demo struct{}
func main() {var str = new(string)fmt.Println("string pointer", str)var num = new(int)fmt.Println("int pointer", num)var de = new(demo)fmt.Println("struct pointer", de)
}
在調(diào)用 new 函數(shù)時(shí),將類型名作為參數(shù)即可創(chuàng)建出這個(gè)類型的指針型對象。
在 Golang 中存在幾種特殊的情況。如使用 new 創(chuàng)建 chan 類型指針型對象,但是仍然需要調(diào)用 make 來給 chan 類型變量分配容量。
package main
import ("fmt""time"
)
func main() {// 創(chuàng)建通道類型指針型對象var ch = new(chan int)// 給對象分配容量,容量為1*ch = make(chan int)// 開啟協(xié)程,從通道中讀取數(shù)據(jù)go func() {fmt.Println(<-(*ch))}()// 向通道中寫入數(shù)據(jù)*ch <- 8time.Sleep(time.Second * 1)
}
如果使用 new 創(chuàng)建通道類型變量后,不使用 make 來分配容量,當(dāng)向通道中寫入信息或從通道中讀取信息時(shí),將會(huì)出現(xiàn)下邊錯(cuò)誤信息:
... [chan send (nil chan)]:
... [chan receive (nil chan)]
雖然 new 關(guān)鍵字可以創(chuàng)建任何類型的指針型對象,但是由于某些特殊類型的對象被創(chuàng)建后,需要進(jìn)行更多的初始化工作,所以 Golang 中引入了 make 函數(shù),通過 make 來創(chuàng)建這些特殊類型的對象。這些特殊類型就是:切片類型、通道類型、字典類型。
另一個(gè)創(chuàng)建變量的方法是調(diào)用內(nèi)建的 new 函數(shù)。表達(dá)式 new(T) 將創(chuàng)建一個(gè) T 類型的匿名變量,初始化為 T 類型的零值,然后返回變量地址,返回的指針類型為*T。
p := new(int) // p, *int 類型, 指向匿名的 int 變量
fmt.Println(*p) // "0"
*p = 2 // 設(shè)置 int 匿名變量的值為 2
fmt.Println(*p) // "2"
用 new 創(chuàng)建變量和普通變量聲明語句方式創(chuàng)建變量沒有什么區(qū)別,除了不需要聲明一個(gè)臨時(shí)變量的名字外,我們還可以在表達(dá)式中使用 new(T) 。換言之, new 函數(shù)類似是一種語法糖,而不是一個(gè)新的基礎(chǔ)概念。
下面的兩個(gè) newInt 函數(shù)有著相同的行為:
func newInt() *int {return new(int)
}
func newInt() *int {var dummy intreturn &dummy
}
每次調(diào)用 new 函數(shù)都是返回一個(gè)新的變量的地址,因此下面兩個(gè)地址是不同的:
p := new(int)
q := new(int)
fmt.Println(p == q) // "false"
當(dāng)然也可能有特殊情況:如果兩個(gè)類型都是空的,也就是說類型的大小是 0,例如struct{}和[0]int,有可能有相同的地址(依賴具體的語言實(shí)現(xiàn))(譯注:請謹(jǐn)慎使用大小為 0 的類型,因?yàn)槿绻愋偷拇笮?0 的話,可能導(dǎo)致 Go 語言的自動(dòng)垃圾回收器有不同的行為,具體請查看runtime.SetFinalizer函數(shù)相關(guān)文檔)。
new 函數(shù)使用通常相對比較少,因?yàn)閷τ诮Y(jié)構(gòu)體來說,直接用字面量語法創(chuàng)建新變量的方法會(huì)更靈活。
由于 new 只是一個(gè)預(yù)定義的函數(shù),它并不是一個(gè)關(guān)鍵字,因此我們可以將 new 名字重新定義為別的類型。例如下面的例子:
func delta(old, new int) int { return new - old }
由于 new 被定義為 int 類型的變量名,因此在 delta 函數(shù)內(nèi)部是無法使用內(nèi)置的 new 函數(shù)的。
2. make
make 也是用于內(nèi)存分配的,但是和 new 不同,它只用于 chan 、 map 以及 slice 的內(nèi)存創(chuàng)建,而且它返回的類型就是這三個(gè)類型本身,而不是他們的指針類型,因?yàn)檫@三種類型就是引用類型,所以就沒有必要返回他們的指針了。
在 Go 語言中, make 函數(shù)的描述如下:
// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
// Slice: The size specifies the length. The capacity of the slice is
// equal to its length. A second integer argument may be provided to
// specify a different capacity; it must be no smaller than the
// length, so make([]int, 0, 10) allocates a slice of length 0 and
// capacity 10.
// Map: An empty map is allocated with enough space to hold the
// specified number of elements. The size may be omitted, in which case
// a small starting size is allocated.
// Channel: The channel's buffer is initialized with the specified
// buffer capacity. If zero, or the size is omitted, the channel is
// unbuffered.func make(t Type, size ...IntegerType) Type
通過上面的代碼可以看出 make 函數(shù)的 t 參數(shù)必須是 chan (通道)、 map (字典)、 slice (切片)中的一個(gè),并且返回值也是類型本身。
make 用于給切片、通道、字典類型變量分配容量空間。給不同的類型變量分配空間時(shí), make 參數(shù)不一樣。下邊來介紹一下不同類型使用 make 時(shí)的不同用法:
注意: make 函數(shù)只用于 map , slice 和 channel ,并且不返回指針。如果想要獲得一個(gè)顯式的指針,可以使用 new 函數(shù)進(jìn)行分配,或者顯式地使用一個(gè)變量的地址。
使用 make 的好處是可以使用很短的代碼初始化一個(gè)長度很大的值,示例如下:
ips := make([]string, 100)
上述代碼表示初始化為一個(gè)元素類型為 string 且長度為 100 的切片值,用 make 函數(shù)初始化的切片值中的每一個(gè)元素值都會(huì)是其元素類型的零值,這里 ips 的 100 個(gè)元素都是空字符串 “”。
使用 make 給切片類型變量分配容量空間:
- 給字符串類型切片分配 4 個(gè)容量空間。
var arr = make([]string,4)
- 給字符串類型切片分配 10 個(gè)容量空間,且設(shè)置切片長度為 5,也就是設(shè)置切片前 5 個(gè)元素已經(jīng)被使用,如果使用
append向切片中追加元素,新追加的元素將會(huì)從第 6 個(gè)位置算起(下標(biāo)為 5)。
var arr = make([]string,5,10)
- 使用
make給通道類型變量分配容量空間,創(chuàng)建了一個(gè)不帶緩沖的通道。
var ch = make(chan int)
- 使用
make給通道類型變量分配容量空間,創(chuàng)建了一個(gè)帶 10 個(gè)緩沖的通道。
var ch = make(chan int, 10)
- 使用
make給字典類型變量分配容量空間
var mp = make(map[string]int)
給字典類型變量分配空間,不需要指定容量,如果在第二個(gè)參數(shù)中指定容量,這個(gè)容量值也會(huì)被忽略。如下邊寫法:
// 容量10被忽略
var mp = make(map[string]int,10)
雖然在給 map 類型變量分配容量時(shí),指定了長度 10,但由于 map 類型變量能夠裝載的數(shù)據(jù)量與系統(tǒng)內(nèi)存有關(guān),所以給 map 類型變量設(shè)置容量的做法將會(huì)被忽略。
請牢記一點(diǎn):make 函數(shù)只能初始化字典(map),切片(slice),通道(chan)類型。
Go 語言中的 new 和 make 主要區(qū)別如下:
make只能用來分配及初始化類型為slice、map、chan的數(shù)據(jù)。new可以分配任意類型的數(shù)據(jù);new分配返回的是指針,即類型*Type。make返回引用,即Type;new分配的空間被清零。make分配空間后,會(huì)進(jìn)行初始化;
總結(jié)
以上是生活随笔為你收集整理的Go 学习笔记(30)— Go 语言 make 和 new 的区别的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电线多少钱一斤啊?
- 下一篇: 王思聪适合饰演哪些电影角色