1. Go 的内存分配
# 概述
Go 拥有 GC,无须担心内存分配和回收
Go 有两个内存分配关键字,new 和 make。它们应用于不同的类型,做不同的工作。
# NEW
# 1. 简述
内建函数 new 本质上说跟其他语言中的同名函数功能一样:new(T) 分配了零值填充
的 T 类型的内存空间
,并且返回其地址
,一个 *T 类型的值。用 Go 的术语说,它返回了一个指针,指向新分配的类型 T 的零值
这意味着使用者可以用 new 创建一个数据结构的实例并且可以直接工作。 如
bytes.Buffer
的文档所述 “Buffer 的零值是一个准备好了的空缓冲。 ” 类似的,
sync.Mutex
也没有明确的构造函数或 Init 方法。取而代之,sync.Mutex 的零值被定义为非锁定的互斥量
# 2. 实例
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
p := new(SyncedBuffer) // Type *SyncedBuffer,已经可以使用
var v SyncedBuffer // Type SyncedBuffer,同上
SyncedBuffer
的值在分配内存或定义之后立刻就可以使用。在这里,变量 p 和 v 都
可以直接使用
# MAKE
内建函数 make(T, args)
与 new(T) 有着不同的功能。它只能创建slice
,map
和 channel
,并且*返回一个有初始值*(非零)的 T 类型,而不是 *T。
本质来讲,导致这三个类型有所不同的原因是指向数据结构的引用在使用前必须被初始化。
例如,一个 slice,是一个包含指向数据(内部 array)的指针,长度和容量的三项描述 符;在这些项目被初始化之前,slice 为 nil。对于 slice,map 和 channel,make 初始化了内部的数据结构,填充适当的值。
例如,make([]int, 10, 100) 分配了 100 个整数的数组,然后用长度 10 和容量 100 创建了 slice 结构指向数组的前 10 个元素。区别是,new([]int) 返回指向新分配的内 存的指针,而零值填充的 slice 结构是指向 nil 的 slice 值。
# New 和 Make 区别
var p *[]int = new([]int) // 分配 slice 结构内存;很少使用
var v []int = make([]int, 100) // v 指向一个新分配的有 100 个整数的数组
var p *[]int = new([]int) // 不必要的复杂例子
*p = make([]int, 100, 100)
v := make([]int, 100) // 更常见
提示
make 仅适用于 map,slice 和 channel,并且返回的不是指针
应当用 new 获得特定的指针
# 构造函数与复合声明
有时零值不能满足需求,必须要有一个用于初始化的构造函数,例如这个来自 os 包的例子
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := new(File)
f.fd = fd
f.name = name
f.dirinfo = nil
f.nepipe = 0
return f
}
- 进阶一
有许多冗长的内容。可以使用复合声明使其更加简洁,每次只用一个表达式创建一个新的实例
func NewFile(fd int, name string) *File {
if fd < 0 {
return nil
}
f := File{fd, name, nil, 0} // ← Create a new File
return &f // 返回 f 的地址
}
- 进阶二
返回本地变量的地址没有问题;在函数返回后,相关的存储区域仍然存在
事实上,从复合声明获取分配的实例的地址更好,因此可以最终将两行缩短到一行
func NewFile(fd int, name string) *File {
return &File{fd, name, nil, 0} // return &File{fd: fd, name: name}
}
在特定的情况下,如果复合声明不包含任何字段,它创建特定类型的零值。表达式 new(File) 和 &File{} 是等价的
从复合声明中获取地址,意味着告诉编译器在堆中分配空间,而不是栈中
复合声明同样可以用于创建 array,slice 和 map
ar := [...]string {Enone: "no error", Einval: "invalid argument"}
sl := []string {Enone: "no error", Einval: "invalid argument"}
ma := map[int]string {Enone: "no error", Einval: "invalid argument"}