make和new的区别

190 阅读3分钟

make和new的区别

文章v2版

make和new的区别

作用对象

make 只能用于slice,chan,map 对这些对象进行初始化

new 用于 golang任意type 自定义类型和 内置数据类型

语义

make(T,args) 初始化内置数据结构(slice,chan,map)

new(T) 根据传入的类型,分配一片零值的内存空间,并返回指向这片内存空间的指针 value *T ,显示的创建了对象的一个指针,而不必使用&T对获取该对象的地址。

官方

Allocation with new

Go has two allocation primitives, the built-in functions new and make. They do different things and apply to different types, which can be confusing, but the rules are simple. Let's talk about new first. It's a built-in function that allocates memory, but unlike its namesakes in some other languages it does not initialize the memory, it only zeros it. That is, new(T) allocates zeroed storage for a new item of type T and returns its address, a value of type *T. In Go terminology, it returns a pointer to a newly allocated zero value of type T.

核心意思:new不会初始化内存,new(T)只是分配一个零值的存储空间,并返回指向该空间的一个地址,这个值的类型*T

Allocation with make

Back to allocation. The built-in function make(T, args) serves a purpose different from new(T). It creates slices, maps, and channels only, and it returns an initialized (not zeroed) value of type T (not *T). The reason for the distinction is that these three types represent, under the covers, references to data structures that must be initialized before use. A slice, for example, is a three-item descriptor containing a pointer to the data (inside an array), the length, and the capacity, and until those items are initialized, the slice is nil. For slices, maps, and channels, make initializes the internal data structure and prepares the value for use

核心意思:make(T,args)只能用于引用数据类型(slices,maps,channels),返回一个已经初始化的非零值对象T。引用数据类型使用之前必须初始。

Q&A

Golang的零值有那些?

类型零值描述
Int,int16,int32,int640
Uint,uint16,uint32,uint640
Float32,float640
Complex64,complex128(0+0i)复数
chan,map,slice,func,interface,ptrnil
String""空串
boolfalse

返回一个零值指针怎么理解?

In Go terminology, it returns a pointer to a newly allocated zero value of type T.

大致意思:在Go术语中,new(T)返回一个指向new刚刚分配的类型为T的零值指针。

  • 代码中p指向了 new分配的类型为map[int]string的一个零值对象的地址

package main

import (
	"fmt"
	"reflect"
)

func main() {
	var p = new(map[int]string)
  
    // p 的类型 *map[int]string
    fmt.Println(reflect.TypeOf(p).String())
  
    // 打印结果 true
    // p 就是零值指针,而*p就 零值指针指向那个对象的值
	fmt.Println(*p == nil) 
}

slice,chan,map,为什么要使用make?

  • 打印的结果都为true
package main

import (
	"fmt"
	"reflect"
)

func main() {
	var sslics []int
	var mmap map[int]string
	var cchan chan int

	// 三者都为nil
	// 打印结果 true true true
	fmt.Println(sslics == nil, mmap == nil, cchan == nil)
}
  • 向一个为nil的map读写数据,将会导致panic,使用make可以指定map的初始空间大小,以容纳元素。如果未指定,则初始空间比较小。
  • 向一个为nil的chan读写数据,会导致 deadlock!,使用make可以初始化chan,并指定chan是缓存chan(make(chan T,size)),还是非缓存chan(make(chan T)
  • 向一个为nil的slice写数据会怎样?
package main

import "fmt"

// 数据正常添加 
func main() {
	var sslices []int
	// append 适用于 切片类型
	sslices = append(sslices, 1)
	fmt.Println(sslices)
}
  • slice为什么要使用make呢?

    • 使用make可以在创建slice时,定义切片的len,cap的大小
    • 使用make可以创建一个非零值的引用对象
package main

import "fmt"

func main() {
	// 分配一个 大小为10的底层数组,并返回一个 长度为0,容量为10的切片
	slices := make([]int, 0, 10)
	slices = append(slices, 1)
    
    // 打印结果 [1] false
	fmt.Println(slices, slices == nil)
}

参考链接

make和new