我为什么要make()或new()?


203

简介文档中有许多段落专门介绍了new()和之间的区别make(),但是实际上,您可以在本地范围内创建对象并将其返回。

您为什么要使用一对分配器?

Answers:


170

您可以做的事情make,您无法做其他任何事情:

  • 建立频道
  • 创建一个预先分配了空间的地图
  • 创建具有预分配空间或len!= cap的切片

证明有一点困难new。使它变得更容易的主要事情是创建指向非复合类型的指针。以下两个功能是等效的。简而言之:

func newInt1() *int { return new(int) }

func newInt2() *int {
    var i int
    return &i
}

41
确实,“新”不能用于创建渠道。但是,我认为要点是:如果将“ new”和“ make”连接到单个内置函数中会发生什么?当然,这样的替换是可能的。既然有可能,问题是:拥有2个内置函数而不是只有1个广义内置函数的客观原因是什么?-您的回答正确地说明了'new'不能用于创建通道/地图/切片,但是它不能提供Go 为何具有'new'和'make'的理由,而不是具有1个广义的alloc + init函数。

5
可以将它们组合起来,甚至是Rob Pike在某一点上提出的:groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion。最终,它没有通过与您答案中给出的原因相似的原因。
埃文·肖

12
有效执行指示new返回零值,而map分配非零类型map,slice或channel。见golang.org/doc/effective_go.html#allocation_new
kristianp

怎么样m := map[string]int{},而不是m := make(map[string]int)?也不需要预先分配大小。
诺曼·马诺斯

165

Go具有多种内存分配和值初始化方式:

&T{...}&someLocalVarnewmake

创建复合文字时也可能发生分配。


new可以用来分配整数等值,&int是非法的:

new(Point)
&Point{}      // OK
&Point{2, 3}  // Combines allocation and initialization

new(int)
&int          // Illegal

// Works, but it is less convenient to write than new(int)
var i int
&i

通过查看以下示例newmake可以看到和之间的区别:

p := new(chan int)   // p has type: *chan int
c := make(chan int)  // c has type: chan int

假设Go没有newand make,但是它具有内置功能NEW。然后,示例代码将如下所示:

p := NEW(*chan int)  // * is mandatory
c := NEW(chan int)

* 将是强制性的,因此:

new(int)        -->  NEW(*int)
new(Point)      -->  NEW(*Point)
new(chan int)   -->  NEW(*chan int)
make([]int, 10) -->  NEW([]int, 10)

new(Point)  // Illegal
new(int)    // Illegal

是的,合并newmake成一个单一的内置功能是可能的。但是,与具有两个内置函数相比,单个内置函数可能会在新的Go程序员中引起更多的混乱。

考虑到以上所有方面,似乎更适合new并将make其分开。


@TorstenBronger我发现new更容易阅读,并表明它int是创建的实例。
Daniel Toebe '16

4
您的意思是写make(Point)make(int)上面提到的最后两行?
吉米·休

27

make函数仅分配和初始化slice,map或chan类型的对象。像一样new,第一个参数是一个类型。但是,它也可能需要第二个参数,即大小。与new不同,make的返回类型与其参数的类型相同,而不是指向它的指针。并且分配的值被初始化(不像new中那样设置为零值)。原因是slice,map和chan是数据结构。它们需要初始化,否则将无法使用。这就是new()和make()需要不同的原因。

以下来自Effective Go的示例非常清楚:

p *[]int = new([]int) // *p = nil, which makes p useless
v []int = make([]int, 100) // creates v structure that has pointer to an array, length field, and capacity field. So, v is immediately usable

1
在中new([]int),它只是为[] int分配内存,但没有初始化,因此它仅返回nil; 不是指向内存的指针,因为它不可用。make([]int)分配并初始化,使其可用,然后返回其地址。
o0omycomputero0o

12
  • new(T)-分配内存,并将其设置为在零值类型Ť ..
    ..that是0INT""字符串nil用于被引用类型(切片地图CHAN

    请注意,引用的类型只是指向某些基础数据结构的指针, 示例将不会创建这些数据结构new(T)
    :如果为slice,则不会创建基础数组,因此new([]int) 返回的指针为空

  • make(T)-为引用的数据类型(slicemapchan)分配内存,并初始化基础数据结构

    示例:如果是slice,则将使用指定的长度和容量创建基础数组
    。请记住,与C不同,数组是Go!中的原始类型!


话虽如此:

  • make(T) 表现像复合文字语法
  • new(T)行为类似var(未初始化变量时)

    func main() {
        fmt.Println("-- MAKE --")
        a := make([]int, 0)
        aPtr := &a
        fmt.Println("pointer == nil :", *aPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *aPtr)
    
        fmt.Println("-- COMPOSITE LITERAL --")
        b := []int{}
        bPtr := &b
        fmt.Println("pointer == nil :", *bPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *bPtr)
    
        fmt.Println("-- NEW --")
        cPtr := new([]int)
        fmt.Println("pointer == nil :", *cPtr == nil)
        fmt.Printf("pointer value: %p\n\n", *cPtr)
    
        fmt.Println("-- VAR (not initialized) --")
        var d []int
        dPtr := &d
        fmt.Println("pointer == nil :", *dPtr == nil)
        fmt.Printf("pointer value: %p\n", *dPtr)
    }
    

    运行程序

    -- MAKE --
    pointer == nil : false
    pointer value: 0x118eff0  # address to underlying array
    
    -- COMPOSITE LITERAL --
    pointer == nil : false
    pointer value: 0x118eff0  # address to underlying array
    
    -- NEW --
    pointer == nil : true
    pointer value: 0x0
    
    -- VAR (not initialized) --
    pointer == nil : true
    pointer value: 0x0
    

    进一步阅读:
    https : //golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make


  • 通过一个例子,事情变得更加清楚。赞成:)
    Sumit Jha

    8

    您需要make()创建通道和地图(以及切片,但是也可以从数组创建切片)。没有其他方法可以制作这些作品,因此您无法make()从词典中删除它们。

    至于new(),我不知道有什么理由可以使用struct语法时就需要它。但是,它确实具有独特的语义,即“创建并返回所有字段均初始化为零值的结构”,这很有用。


    1
    因此应该避免使用新的脚本,而只喜欢使用Struct语法
    CommonSenseCode

    8

    除了Effective Go中解释的所有内容之外,new(T)和之间的主要区别在于&T{}后者明确执行堆分配。但是,应注意,这取决于实现方式,因此可能会发生变化。

    比较make起来new没有什么意义,因为两者执行的功能完全不同。但这在链接的文章中有详细说明。


    10
    该要求&T{}明确执行堆分配不基于任何规格AFAIK。实际上,我相信转义分析已经尽可能地将与* T相同的方式将* T保留在堆栈中new(T)
    zzzz 2012年

    6

    new(T):它返回一个指向类型T指针,指针的类型为* T,它分配内存并将其清零。new(T)等同于&T {}

    make(T):它返回类型T初始化值,它分配并初始化内存。它用于切片,地图和通道。

    By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
    Licensed under cc by-sa 3.0 with attribution required.