new与make的差异及使用策略
- 作者
- Name
- 青玉白露
- Github
- @white0dew
- Modified on
- Reading time
- 4 分钟
阅读:.. 评论:..
Go语言在数据结构初始化时,经常会用到new
和make
两个内置函数。
这两个函数都能分配内存空间,但它们具体有什么不同?而在编码实践中,如何正确高效地使用new
和make
呢?
new
的内涵
让我们先来探讨new
函数:
func new(Type) *Type
new函数用于分配内存。它的第一个参数是类型而非值,返回的是指向该类型新分配的零值的指针。
通过官方定义,我们可以了解到new
的以下特性:
- 用于内存分配,分配的内存中存储的是类型的零值。
- 函数只接受一个参数——即你要分配的内存类型。Go语言中的任何类型都可作为
new
的参数。 - 返回值为指针。
如下代码示例,展示了new(T)
与声明变量并取地址的等效性:
// 方式一 ptr := new(T) // 方式二 var t T ptr := &t
需要注意的是,Go的new
与C++中的new
并不相同:
- Go的
new
所分配的内存可能位于栈上,也可能位于堆上,而C++的new
总是在堆上分配内存。 - Go的
new
只能初始化为类型的零值,不支持显式初始化。而C++的new
则可以在分配时指定初始值。 - 在Go中,由于没有构造函数的概念,
new
不会调用构造函数。而在C++中,new
会调用相应类型的构造函数。
make
的本质
现在,让我们来解析make
函数:
func make(t Type, size ...IntegerType) Type
make
函数专门用来分配并初始化类型为切片(slice)、映射(map)、通道(chan)的对象(仅限这三种)。与new
相似,make
的第一个参数是类型。不同的是,make
返回的类型与其参数相同,并非指针。其结果的规格取决于类型:切片(Slice): 第二个参数指定长度。切片的容量等于其长度。可以提供第二个整数参数来指定不同的容量;容量不能小于长度。例如,
make([]int, 0, 10)
分配了一个大小为10的底层数组,并返回一个长度为0、容量为10且基于该数组的切片。映射(Map): 分配一个空映射,足以容纳指定数量的元素。大小参数可以省略,这样就会分配一个小的初始大小。
通道(Channel): 通道的缓冲区将以指定的容量初始化。如果为零或省略大小,通道将是无缓冲的。
从make
的定义中我们可知:
- 既分配又初始化内存。
- 只适用于切片(slice)、映射(map)和通道(chan)三种类型。
- 返回的是实际类型,而非指向该类型的指针。
常见疑惑的解答
在使用new
和make
时,让我们来解答几个常见问题:
- 为何切片(slice)、映射(map)和通道(chan)需要专门的
make
函数来初始化? 这是因为这三种类型在使用前必须初始化。未初始化的切片(slice)、映射(map)和通道(chan)其值为零值,即为nil
。其中,nil
映射无法添加元素,会导致程序崩溃;nil
通道无法进行数据传递,会导致阻塞。而nil
切片虽然可以通过append
函数扩展,但通常我们需要自定义切片的长度或容量,这就需要使用make
。 - 是否可以使用
new
创建切片(slice)、映射(map)和通道(chan)? 可以,但创建出的将是nil
值。例如:
a := *new([]int) b := *new(map[string]int) c := *new(chan int)
这些nil
值的类型在实践中无法直接使用。
- 为什么为
nil
的切片(slice)调用append
也可以正常工作? 对于nil
切片,append
会自动为其底层数组进行扩容,分配必要的内存空间,然后赋值给原先的nil
切片。
最优使用建议
- 尽量避免使用
new
。 - 在定义和初始化切片(slice)、映射(map)和通道(chan)时,优先考虑使用
make
。