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("p1 point to --> %#v \n ", *p1) //0
var p2 *int
i := 0
p2 = &i
fmt.Printf("p2 --> %#v \n ", p2) //(*int)(0xc42000e278)
fmt.Printf("p2 point to --> %#v \n ", *p2) //0

上面的代码是等价的,new(int) 将分配的空间初始化为 int 的零值,也就是 0,并返回 int 的指针,这和直接声明指针并初始化的效果是相同的。

make 只能用于 slice,map,channel

make 只能用于 slice,map,channel 三种类型,make(T, args) 返回的是初始化之后的 T 类型的值,这个新值并不是 T 类型的零值,也不是指针 *T,是经过初始化之后的 T 的引用。

var s1 []int
if s1 == nil {
 fmt.Printf("s1 is nil --> %#v \n ", s1) // []int(nil)
}
s2 := make([]int, 3)
if s2 == nil {
 fmt.Printf("s2 is nil --> %#v \n ", s2)
} else {
 fmt.Printf("s2 is not nill --> %#v \n ", s2)// []int{0, 0, 0}
}

slice 的零值是 nil,使用 make 之后 slice 是一个初始化的 slice,即 slice 的长度、容量、底层指向的 array 都被 make 完成初始化,此时 slice 内容被类型 int 的零值填充,形式是 [0 0 0],map 和 channel 也是类似的。

var m1 map[int]string
if m1 == nil {
 fmt.Printf("m1 is nil --> %#v \n ", m1) //map[int]string(nil)
}
m2 := make(map[int]string)
if m2 == nil {
 fmt.Printf("m2 is nil --> %#v \n ", m2)
} else {
 fmt.Printf("m2 is not nill --> %#v \n ", m2) map[int]string{}
}
var c1 chan string
if c1 == nil {
 fmt.Printf("c1 is nil --> %#v \n ", c1) //(chan string)(nil)
}
c2 := make(chan string)
if c2 == nil {
 fmt.Printf("c2 is nil --> %#v \n ", c2)
} else {
 fmt.Printf("c2 is not nill --> %#v \n ", c2)//(chan string)(0xc420016120)
}

make(T, args) 返回的是 T 的 引用

如果不特殊声明,go 的函数默认都是按值穿参,即通过函数传递的参数是值的副本,在函数内部对值修改不影响值的本身,但是 make(T, args) 返回的值通过函数传递参数之后可以直接修改,即 map,slice,channel 通过函数穿参之后在函数内部修改将影响函数外部的值。

func modifySlice(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 也是如此。

func modifyMap(m map[int]string) {
 m[0] = "string"
}
func modifyChan(c chan string) {
 c <- "string"
}
m2 := make(map[int]string)
if m2 == nil {
 fmt.Printf("m2 is nil --> %#v \n ", m2)
} else {
 fmt.Printf("m2 is not nill --> %#v \n ", m2) //map[int]string{}
}
modifyMap(m2)
fmt.Printf("m2 is not nill --> %#v \n ", m2) // map[int]string{0:"string"}
c2 := make(chan string)
if c2 == nil {
 fmt.Printf("c2 is nil --> %#v \n ", c2)
} else {
 fmt.Printf("c2 is not nill --> %#v \n ", c2)
}
go modifyChan(c2)
fmt.Printf("c2 is not nill --> %#v ", <-c2) //"string"

很少需要使用 new

type Foo struct {
 name string
 age int
}
var foo1 Foo
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)
var foo5 *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 在下面自动做了转换。

因而可以直接使用 struct literal 的方式创建对象,能达到和 new 创建是一样的情况而不需要使用 new。

小结

new(T) 返回 T 的指针 *T 并指向 T 的零值。

make(T) 返回的初始化的 T,只能用于 slice,map,channel。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Go语言中new()和 make()的区别详解

    概述 Go 语言中的 new 和 make 一直是新手比较容易混淆的东西,咋一看很相似.不过解释两者之间的不同也非常容易. new 的主要特性 首先 new 是内建函数,你可以从 http://golang.org/pkg/builtin/#new 这儿看到它,它的定义也很简单: 复制代码 代码如下: func new(Type) *Type 官方文档对于它的描述是: 复制代码 代码如下: 内建函数 new 用来分配内存,它的第一个参数是一个类型,不是一个值,它的返回值是一个指向新分配类型零值的

  • 深入讲解Go语言中函数new与make的使用和区别

    前言 本文主要给大家介绍了Go语言中函数new与make的使用和区别,关于Go语言中new和make是内建的两个函数,主要用来创建分配类型内存.在我们定义生成变量的时候,可能会觉得有点迷惑,其实他们的规则很简单,下面我们就通过一些示例说明他们的区别和使用,话不多说了,来一起看看详细的介绍吧. 变量的声明 var i int var s string 变量的声明我们可以通过var关键字,然后就可以在程序中使用.当我们不指定变量的默认值时,这些变量的默认值是他们的零值,比如int类型的零值是0,st

  • GO语言make()分配用法实例

    本文实例讲述了GO语言make()分配用法.分享给大家供大家参考.具体分析如下: make()分配:内部函数 make(T, args) 的服务目的和 new(T) 不同. 它只生成切片,映射和程道,并返回一个初始化的(不是零)的,type T的,不是 *T 的值. 这种区分的原因是,这三种类型的数据结构必须在使用前初始化. 比如切片是一个三项的描述符,包含数据指针(数组内),长度,和容量:在这些项初始化前,切片为 nil . 对于切片.映射和程道,make初始化内部数据结构,并准备要用的值.

  • Go语言里的new函数用法分析

    本文实例讲述了Go语言里的new函数用法.分享给大家供大家参考.具体如下: 表达式 new(T) 分配了一个零初始化的 T 值,并返回指向它的指针. var t *T = new(T) 或 t := new(T) 代码如下: 复制代码 代码如下: package main import "fmt" type Vertex struct {     X, Y int } func main() {     v := new(Vertex)     fmt.Println(v)     v

  • 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 &quo

  • Golang中Set类型的实现方法示例详解

    前言 本篇主要给大家讲述了如何利用Go语言的语法特性实现Set类型的数据结构,分享出来供大家参考学习,话不多说了,来一起看看详细的介绍吧. 需求 对于Set类型的数据结构,其实本质上跟List没什么多大的区别.无非是Set不能含有重复的Item的特性,Set有初始化.Add.Clear.Remove.Contains等操作.接下来看具体的实现方式分析吧. 实现 仍然按照已有的编程经验来联想如何实现基本Set功能,在Java中很容易知道HashSet的底层实现是HashMap,核心的就是用一个常量

  • Mongoose中document与object的区别示例详解

    前言 本文主要给大家总结介绍了关于Mongoose中document与object区别的相关内容,分享出来供大家参考学习,其实这个问题其实是mongoose非常常见的问题,经常有很多以前没遇到这个问题的人都会被这个问题弄得怀疑人生. 我们先介绍一些问题的背景. 先看下面一段代码: router.get('/', function(req, res, next) { // res.render('index', { title: 'Express' }); const model = mongoo

  • Java中class和Class的区别示例详解

    目录 一.class与Class区别 二.Class介绍 三.如何得到Class对象 1.通过getClass()方法获取到Class对象 2.通过forName()方法获取到Class对象 3.类.class获得Class对象(类字面常量) 四.Class常用方法 总结 一.class与Class区别 class是Java中的关键字,如public class Xxx 或者 class Xxx ,在声明Java类时使用. 而Class是一个类. 我们通常认为类是对象的抽象和集合,Class就相

  • BeanFactory和FactoryBean的区别示例详解

    目录 正文 BeanFactory和FactoryBean的区别 1.BeanFactory 2.FactoryBean 正文 这个之前经常会遇到别人问 但是一直不是很能理解 工作开发中我对于bean的使用比较少 就是偶尔启动出错才会出现 可能是水平有限 但是bean 也是非常核心的问题 书到用时方恨少 且看且珍惜 BeanFacotry是spring中比较原始的Factory. 如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持spring

  • React元素与组件的区别示例详解

    目录 从问题出发 元素与组件 元素 组件 问题如何解决 自定义内容 第一种实现方式 第二种实现方式 第三种实现方式 从问题出发 我被问过这样一个问题: 想要实现一个 useTitle 方法,具体使用示例如下: function Header() { const [Title, changeTitle] = useTitle(); return ( <div onClick={() => changeTitle('new title')}> <Title /> </div

  • Golang中结构体映射mapstructure库深入详解

    目录 mapstructure库 字段标签 内嵌结构 未映射字段 Metadata 弱类型输入 逆向转换 解码器 示例 在数据传递时,需要先编解码:常用的方式是JSON编解码(参见<golang之JSON处理>).但有时却需要读取部分字段后,才能知道具体类型,此时就可借助mapstructure库了. mapstructure库 mapstructure可方便地实现map[string]interface{}与struct间的转换:使用前,需要先导入库: go get github.com/m

  • C++ 中引用与指针的区别实例详解

    C++ 中引用与指针的区别实例详解 引用是从C++才引入的,在C中不存在.为了搞清楚引用的概念,得先搞明白变量的定义及引用与变量的区别,变量的要素一共有两个:名称与空间. 引用不是变量,它仅仅是变量的别名,没有自己独立的空间,它只符合变量的"名称"这个要素,而"空间"这个要素并不满足.换句话说,引用需要与它所引用的变量共享同一个内存空间,对引用所做的改变实际上是对所引用的变量做出修改.并且引用在定义的时候就必须被初始化.     参数传递的类型及相关要点: 1 按值

  • C++ 中cerr和cout的区别实例详解

    C++ 中cerr和cout的区别实例详解 前言: cerrThe object controls unbuffered insertions to the standard error output as a byte stream. Once the object is nstructed, the expression cerr.flags & unitbuf is nonzero. Example // iostream_cerr.cpp // compile with: /EHsc /

  • java中 String和StringBuffer的区别实例详解

    java中 String和StringBuffer的区别实例详解 String: 是对象不是原始类型.            为不可变对象,一旦被创建,就不能修改它的值.            对于已经存在的String对象的修改都是重新创建一个新的对象,然后把新的值保存进去.            String 是final类,即不能被继承. StringBuffer: 是一个可变对象,当对他进行修改的时候不会像String那样重新建立对象            它只能通过构造函数来建立,  

随机推荐