# 使用new进行分配
使用new进行分配
Go有两个分配原语,内建函数new和make。它们所做的事情有所不同,并且用于不同的类型。这会有些令人混淆,但规则其实很简单。我们先讲下new。这是一个用来分配内存的内建函数,但是不像在其它语言中,它并不初始化内存,只是将其置零。也就是说,new(T)会为T类型的新项目,分配被置零的存储,并且返回它的地址,一个类型为*T的值。在Go的术语中,其返回一个指向新分配的类型为T,值为零的指针。
由于new返回的内存是被置零的,这会有助于你将数据结构设计成,每个类型的零值都可以使用,而不需要进一步初始化。这意味着,数据结构的用户可以使用new来创建数据,并正确使用。例如,bytes.Buffer的文档说道,"Buffer的零值是一个可以使用的空缓冲"。类似的,sync.Mutex没有显式的构造器和Init方法。相反的,sync.Mutex的零值被定义为一个未加锁的互斥。
“零值可用”的属性是可以传递的。考虑这个类型声明。
```
type SyncedBuffer struct {
lock sync.Mutex
buffer bytes.Buffer
}
```
SyncedBuffer类型的值也可以在分配或者声明之后直接使用。在下一个片段中,p和v都不需要进一步的处理便可以正确地工作。
```
p := new(SyncedBuffer) // type *SyncedBuffer
var v SyncedBuffer // type SyncedBuffer
```
## 构造器和复合文字
有时候零值并不够好,需要一个初始化构造器(constructor),正如这个源自程序包os的例子。
```go
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
}
```
有许多这样的模版。我们可以使用*复合文字(composite literal)*进行简化,其为一个表达式,在每次求值的时候会创建一个新实例。
```go
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{}是等价的。
复合文字还可用于arrays,slices和maps,域标号使用适当的索引或者map key。下面的例子中,不管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"}
##使用make进行分配
回到分配的话题。内建函数make(T,args)与new(T)的用途不一样。它只用来创建slice,map和channel,并且返回一个初始化的(而不是置零),类型为T的值(而不是*T)。之所以有所不同,是因为这三个类型的背后是象征着,对使用前必须初始化的数据结构的引用。例如,slice是一个三项描述符,包含一个指向数据(在数组中)的指针,长度,以及容量,在这些项被初始化之前,slice都是nil的。对于slice,map和channel,make初始化内部数据结构,并准备好可用的值。例如,
make([]int, 10, 100)
分配一个有100个int的数组,然后创建一个长度为10,容量为100的slice结构,并指向数组前10个元素上。(当创建slice时,容量可以省略掉,更多信息参见slice章节。)对应的,new([]int)返回一个指向新分配的,被置零的slice结构体的指针,即指向nilslice值的指针。
这些例子阐释了new和make之间的差别。
```
var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful
var v []int = make([]int, 100) // the slice v now refers to a new array of 100 ints
// Unnecessarily complex:
var p *[]int = new([]int)
*p = make([]int, 100, 100)
// Idiomatic:
v := make([]int, 100)
记住make只用于map,slice和channel,并且不返回指针。要获得一个显式的指针,使用new进行分配,或者显式地使用一个变量的地址。
```
- 命令行库cobra
- 用户路径检测go-homedir
- 配置解决方案viper(cobra配置用)
- 高效结构化日志库zap
- RPC框架grpc
- mongdb操作mgo
- ORM库xorm
- GRPCrest接口grpcgateway
- 使用gogoproto时grpcgateway的protobuf和json转换方法
- sync.Map
- zmq
- gogoproto
- go类型转换和类型断言
- go select用法详解以及定时器
- go并发资源竞争
- 官方命令行库flag
- 配置文件解析器 robig/config
- interface {} 接口
- goroutine && channel
- go 命名
- 类型switch
- 数据
- 初始化
- 指针方法 && 值方法
- 内嵌
- mqtt go实现
- grpc middleware