golang中make和new的区别示例详解
前言
本文主要给大家介绍了关于golang中make和new区别的相关内容,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍:
new和make都可以用来分配空间,初始化类型,但是它们确有不同。
new(T)返回的是T的指针
new(T)为一个T类型新值分配空间并将此空间初始化为T的零值,返回的是新值的地址,也就是T类型的指针*T,该指针指向T的新分配的零值。
p1:=new(int)
fmt.Printf("p1-->%#v\n",p1)//(*int)(0xc42000e250)
fmt.Printf("p1pointto-->%#v\n",*p1)//0
varp2*int
i:=0
p2=&i
fmt.Printf("p2-->%#v\n",p2)//(*int)(0xc42000e278)
fmt.Printf("p2pointto-->%#v\n",*p2)//0
上面的代码是等价的,new(int)将分配的空间初始化为int的零值,也就是0,并返回int的指针,这和直接声明指针并初始化的效果是相同的。
make只能用于slice,map,channel
make只能用于slice,map,channel三种类型,make(T,args)返回的是初始化之后的T类型的值,这个新值并不是T类型的零值,也不是指针*T,是经过初始化之后的T的引用。
vars1[]int
ifs1==nil{
fmt.Printf("s1isnil-->%#v\n",s1)//[]int(nil)
}
s2:=make([]int,3)
ifs2==nil{
fmt.Printf("s2isnil-->%#v\n",s2)
}else{
fmt.Printf("s2isnotnill-->%#v\n",s2)//[]int{0,0,0}
}
slice的零值是nil,使用make之后slice是一个初始化的slice,即slice的长度、容量、底层指向的array都被make完成初始化,此时slice内容被类型int的零值填充,形式是[000],map和channel也是类似的。
varm1map[int]string
ifm1==nil{
fmt.Printf("m1isnil-->%#v\n",m1)//map[int]string(nil)
}
m2:=make(map[int]string)
ifm2==nil{
fmt.Printf("m2isnil-->%#v\n",m2)
}else{
fmt.Printf("m2isnotnill-->%#v\n",m2)map[int]string{}
}
varc1chanstring
ifc1==nil{
fmt.Printf("c1isnil-->%#v\n",c1)//(chanstring)(nil)
}
c2:=make(chanstring)
ifc2==nil{
fmt.Printf("c2isnil-->%#v\n",c2)
}else{
fmt.Printf("c2isnotnill-->%#v\n",c2)//(chanstring)(0xc420016120)
}
make(T,args)返回的是T的引用
如果不特殊声明,go的函数默认都是按值穿参,即通过函数传递的参数是值的副本,在函数内部对值修改不影响值的本身,但是make(T,args)返回的值通过函数传递参数之后可以直接修改,即map,slice,channel通过函数穿参之后在函数内部修改将影响函数外部的值。
funcmodifySlice(s[]int){
s[0]=1
}
s2:=make([]int,3)
fmt.Printf("%#v",s2)//[]int{0,0,0}
modifySlice(s2)
fmt.Printf("%#v",s2)//[]int{1,0,0}
这说明make(T,args)返回的是引用类型,在函数内部可以直接更改原始值,对map和channel也是如此。
funcmodifyMap(mmap[int]string){
m[0]="string"
}
funcmodifyChan(cchanstring){
c<-"string"
}
m2:=make(map[int]string)
ifm2==nil{
fmt.Printf("m2isnil-->%#v\n",m2)
}else{
fmt.Printf("m2isnotnill-->%#v\n",m2)//map[int]string{}
}
modifyMap(m2)
fmt.Printf("m2isnotnill-->%#v\n",m2)//map[int]string{0:"string"}
c2:=make(chanstring)
ifc2==nil{
fmt.Printf("c2isnil-->%#v\n",c2)
}else{
fmt.Printf("c2isnotnill-->%#v\n",c2)
}
gomodifyChan(c2)
fmt.Printf("c2isnotnill-->%#v",<-c2)//"string"
很少需要使用new
typeFoostruct{
namestring
ageint
}
varfoo1Foo
fmt.Printf("foo1-->%#v\n",foo1)//main.Foo{age:0,name:""}
foo1.age=1
fmt.Println(foo1.age)
foo2:=Foo{}
fmt.Printf("foo2-->%#v\n",foo2)//main.Foo{age:0,name:""}
foo2.age=2
fmt.Println(foo2.age)
foo3:=&Foo{}
fmt.Printf("foo3-->%#v\n",foo3)//&main.Foo{age:0,name:""}
foo3.age=3
fmt.Println(foo3.age)
foo4:=new(Foo)
fmt.Printf("foo4-->%#v\n",foo4)//&main.Foo{age:0,name:""}
foo4.age=4
fmt.Println(foo4.age)
varfoo5*Foo=new(Foo)
fmt.Printf("foo5-->%#v\n",foo5)//&main.Foo{age:0,name:""}
foo5.age=5
fmt.Println(foo5.age)
foo1和foo2是同样的类型,都是Foo类型的值,foo1是通过var声明,Foo的filed自动初始化为每个类型的零值,foo2是通过字面量的完成初始化。
foo3,foo4和foo5是一样的类型,都是Foo的指针*Foo。
但是所有foo都可以直接使用Foo的filed,读取或修改,为什么?
如果x是可寻址的,&x的filed集合包含m,x.m和(&x).m是等同的,go自动做转换,也就是foo1.age和foo3.age调用是等价的,go在下面自动做了转换。
因而可以直接使用structliteral的方式创建对象,能达到和new创建是一样的情况而不需要使用new。
小结
new(T)返回T的指针*T并指向T的零值。
make(T)返回的初始化的T,只能用于slice,map,channel。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。
