Go语言new()和 make()区别


Go 语言 new 和 make  都是内建函数
//golang.org/pkg/builtin/#new
//golang.org/pkg/builtin/#make
变量的类型
先分清楚 引用类型 和 值类型
值类型
golang 内置原始数据类型是值类型
如int float string bool 值类型是不可变的  操作返回一个新创建的值
把这些值传递给函数时 创建的是值的副本 作为参数传递给函数的都是副本
func main(){
  s := "a"
  modify(s)
  fmt.Println(s)//output: "a"
}
func modify(s string)string{
  s = "888888888"
}
修改值类型 只是生成其副本 所以值类型在多线程中是安全的
引用类型
切片 map 接口 函数类型 及chan 都是引用类型  引用类型的修改 影响任何引用它的变量
引用类型之所以可引用 是因为创建引用类型的变量是一个标头值
标头值里包含一个指针 指向底层的数据结构 在函数中传递引用类型时 传递的是这个标头值的副本 它所指向的底层结构并没有被复制传递 这也是引用类型传递高效的原因
func main(){
  ages := map[string]int{"s":12}
  modify(ages)
  fmt.Println(ages)//output: map[s:77]
}
func modify(m map[string]int){
  m["s"] = 77
}
函数 modify 修改 会影响 ages 的值
结构类型
用 var 关键字声明一个结构体类型的变量
type person struct{
  age int
  name string
}
var p person
这种 var 声明的方式 会对结构体 person 里的数据类型默认初始化 即使用 其类型的零值
函数传参是值传递 所以对于结构体来说也不例外 结构体传递的是其本身以及里面的值的拷贝
type Person struct {
    age int
    name string
}
func modify(p Person){
    p.age  = 88
}
func main(){
    jim := Person{10,"jim"}
    fmt.Println(jim)//{10 jim}
    modify(jim)
    fmt.Println(jim)//{10 jim}
}
输出一样  验证传递的是值的副本
要修改 age 的值 通过传递结构体的指针
type Person struct {
    age int
    name string
}
// *Person 是针对结构体实例的操作
func modify(p *Person){
    p.age  = 88
}
func main(){
    jim := Person{10,"jim"}
// var jim Person = Person{10, "jim"}
    fmt.Println(jim)//{10 jim}
    modify(&jim)// &jim 指向的是实例
    fmt.Println(jim)//{88 jim}// * 号在定义时使用 &在运算时使用 *这种类型在运算时映射的值是&
}
自定义类型
type Duration int64
Go是强类型语言 所以 int64 与 Duration 不能直接赋值
变量的声明
var i int
var s string
变量的声明 通过 var 关键字 var 声明的变量的值 是其默认的零值
类型     默认零值
int     0
string     ""
引用类型     nil
引用类型赋值 要先分配内存空间
*int 是声明一个值类型 int的引用类型
*i  是对值类型的引用类型进行运算
func main(){
  var i *int
  *i = 10
  fmt.Println(*i)
}
运行时的 paninc : runtime error: invalid memory address or nil pointer dereference
对于引用类型的变量 不光要声明 还要为它分配内存空间 否则值放在哪去呢
值类型的声明则不需要手动分配内存空间  因为默认已经分配好了 值类型有默认零值
new 和 make分配内存
func main(){
  var i *int
  i = new(int)
  *i = 10
  fmt.Println(*i)//10
}
//The new built-in function allocates memory.
//The first argument is a type,not a value, and the value returned is a pointer to a newly allocated zero value of that type.
func new(Type) *Type
new 是一个分配内存的内置函数  new的参数是一个类型 new的返回值是一个指针 该指针指向参数的默认零值
func main(){
//u 类型 *user 因为new函数返回的是一个指向参数类型 默认零值的指针
// 这种方式和上文中 u := user{} //&u 的方式 是一样的
    u := new(user)
    u.lock.Lock()
    u.name = "sssssssss"
    u.lock.Unlock()
    fmt.Println(u)
}
type user struct {
    lock sync.Mutex
    name string
    age int
}这就是 new 返回的永远是类型的指针 指向分配类型的内存地址
make 也用于内存分配 和 new 不同 它只用于chan/map/切片的内存创建 且返回的类型是这三个类型本身 不是它们的指针类型
因为chan/map/切片已经是引用类型了 没必要返回他们的指针了
make文档
The make built-in function allocates and initializes an object of type slice, map, or chan (only).Like new,the argument is a type, not a value.Unlike new, make's return type is the same as the type of its argument,not a pointer to it. The specification of the result depends on the type:
Slice: the size specifies the length. The capacity of the slice is equal to its length.A second integer argument may be provided to specify a different capacity; it must be no smaller than the length, so make([]int,0,10) allocates a slice of length 0 and capacity 10.
Map: An empty map is allocated with enough space to hold the specified number of elements.The size may be omitted,in which case a small starting size is allocated.
Channel: The channel's buffer is initialized with the specified buffer capacity. If zero, or the size is omitted, the channel is unbuffered.
func make(t Type, size ...IntegerType) Type
make 为 slice/map/chan初始化 分配内存
和new 类似 第一个参数必须是一个类型
和 new 不同的是 new 返回的是参数类型的指针 指针指向的该参数的默认零值
而make返回的是传入类型相同 并不是指针 因为 slice / map / chan 本身就是引用类型
func new(Type) *Type
new 分配内存 第一个参数是一个类型 不是一个值 返回值是一个指向新分配类型零值的指针
根据描述 自己实现一个类似 new 的功能
func newInt() *int {
  var i int
  return &i
}
someInt := newInt()//功能跟 someInt := new(int) 一模一样
所以在自己定义 new 开头的函数时 出于约定也应该返回类型的指针

make主要特性

make 定义比 new 多了一个参数 返回值也不同
func make(Type, size IntegerType) Type
内建函数 make 用来为 slice map 或 chan 类型分配内存和初始化一个对象(注意 只能用在这三种类型上)
跟 new 类似 第一个参数也是一个类型而不是一个值
跟 new 不同 make 返回类型的引用而不是指针 而返回值也依赖于具体传入的类型
Slice 第二个参数 size 指定了它的长度 它的容量和长度相同
可以传入第三个参数来指定不同的容量值 但必须不能比长度值小
比如 make([]int, 0, 10)
Map 根据 size 大小来初始化分配内存 不过分配后的 map 长度为 0
如果 size 被忽略了 那么会在初始化分配内存时分配一个小尺寸的内存
Channel  管道缓冲区依据缓冲区容量被初始化 如果容量为 0 或者忽略容量 管道是没有缓冲区的
总结
new  是初始化一个指向类型的指针(*T)
make 是为slice/map/chan 初始化并返回引用(T)