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。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。