这是我参与11月更文挑战的第24天,活动详情查看:2021最后一次更文挑战
使用new分配地址
Go 有两个分配语句,内置函数 new 和 make。 它们做不同的事情并适用于不同的类型,这可能会令人困惑,但其实规则很简单。 让我们先谈谈new。 它是一个分配内存的内置函数,但与其他一些语言中的同名函数不同,它不会初始化内存,只会将其归零。 也就是说,new(T) 为类型为 T 的新项分配零存储并返回其地址,类型为 *T 的值。 在 Go 术语中,它返回一个指向新分配的 T 类型零值的指针。
由于 new 返回的内存已归零,因此在设计数据结构时,考虑好每种类型的零值,这样当你使用new的是胡无需进一步初始化即可使用。 这意味着数据结构的用户可以使用 new 创建一个数据并直接开始工作。 例如,bytes.Buffer 的文档写到:“Buffer 的零值是一个准备使用的空缓冲区”。 同样,sync.Mutex 没有显式构造函数或 Init 方法。 相反,sync.Mutex 的零值被定义为未锁定的互斥锁。
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
SyncedBuffer 类型的值也可以在分配或声明后立即使用。 在下一个片段中, p 和 v 无需进一步安排即可正常工作。
构造函数和复合字面量
有时零值不够好,需要初始化构造函数,如从包 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}
return &f
}
请注意,与 C 不同,返回局部变量的地址是完全可以的; 与变量关联的地址在函数返回后仍然存在。 事实上,每次计算复合字面量的地址时都会分配一个新实例,因此我们可以将最后两行组合起来。
return &File{fd, name, nil, 0}
复合文字的字段按顺序排列,并且必须全部存在。 但是,通过将元素显式标记为 field:value 对,初始值设定项可以按任何顺序出现,而缺少的则作为各自的零值保留。 因此我们可以写成
return &File{fd: fd, name: name}
作为一种限制情况,如果复合文字根本不包含任何字段,它会为该类型创建一个零值。 表达式 new(File) 和 &File{} 是等价的。
还可以为数组、切片和映射创建复合文字,字段标签是索引或映射键(视情况而定)。 在这些示例中,只要 Enone、Eio 和 Einval 的值不同,初始化都会起作用。
a := [...]string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
s := []string {Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
m := map[int]string{Enone: "no error", Eio: "Eio", Einval: "invalid argument"}
\