zoukankan      html  css  js  c++  java
  • Golang中new和make的区别

    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
    

    构造函数与复合字面

    有时零值还不够好,这时就需要一个初始化构造函数,如来自 os 包中的这段代码所示。

    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
    }
    

    这里显得代码过于冗长。我们可通过复合字面来简化它, 该表达式在每次求值时都会创建新的实例。

    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}
    

    复合字面的字段必须按顺序全部列出。但如果以 字段:值 对的形式明确地标出元素,初始化字段时就可以按任何顺序出现,未给出的字段值将赋予零值。 因此,我们可以用如下形式:

    return &File{fd: fd, name: name}
    

    少数情况下,若复合字面不包括任何字段,它将创建该类型的零值。表达式 new(File) 和 &File{} 是等价的。

    复合字面同样可用于创建数组、切片以及映射,字段标签是索引还是映射键则视情况而定。 在下例初始化过程中,无论 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)。它只用于创建切片、映射和信道,并返回类型为 T(而非 *T)的一个已初始化 (而非置零)的值。 出现这种用差异的原因在于,这三种类型本质上为引用数据类型,它们在使用前必须初始化。 例如,切片是一个具有三项内容的描述符,包含一个指向(数组内部)数据的指针、长度以及容量, 在这三项被初始化之前,该切片为 nil。对于切片、映射和信道,make 用于初始化其内部的数据结构并准备好将要使用的值。例如,

    make([]int, 10, 100)

    会分配一个具有100个 int 的数组空间,接着创建一个长度为10, 容量为100并指向该数组中前10个元素的切片结构。(生成切片时,其容量可以省略,更多信息见切片一节。) 与此相反,new([]int) 会返回一个指向新分配的,已置零的切片结构, 即一个指向 nil 切片值的指针。

    下面的例子阐明了 new 和 make 之间的区别:

    var p *[]int = new([]int)       // 分配切片结构;*p == nil;基本没用
    var v  []int = make([]int, 100) // 切片 v 现在引用了一个具有 100 个 int 元素的新数组
    
    // 没必要的复杂:
    var p *[]int = new([]int)
    *p = make([]int, 100, 100)
    
    // 习惯用法:
    v := make([]int, 100)
    

    请记住,make 只适用于映射、切片和信道且不返回指针。若要获得明确的指针, 请使用 new 分配内存。

  • 相关阅读:
    formData实现图片上传
    input[type='file']样式美化及实现图片预览
    第一个Vue插件从封装到发布
    lastIndex对正则结果的影响
    使用图片地图减少HTTP请求数量
    实现文字颜色渐变
    vue-cli中如何引入jquery
    事件处理详解
    图片载入状态判断及实现百分比效果loading
    页面加载时触发的事件及顺序
  • 原文地址:https://www.cnblogs.com/yahoo17/p/13182782.html
Copyright © 2011-2022 走看看