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

时间:2012-02-16 23:42:55

标签: go

介绍文档专门用许多段落来解释new()make()之间的区别,但在实践中,您可以在局部范围内创建对象并返回它们。

为什么要使用这对分配器?

9 个答案:

答案 0 :(得分:140)

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没有newmake,但它具有内置函数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程序员比两个内置函数更加混乱。

考虑到以上所有要点,newmake似乎更合适。

答案 1 :(得分:137)

你可以用make做的事情,你不能做任何其他事情:

  • 创建频道
  • 创建预分配空间的地图
  • 创建一个预分配空格或使用len!= cap
  • 的切片

证明new是合理的。它更容易的主要是创建指向非复合类型的指针。 以下两个功能是等效的。一个更简洁:

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

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

答案 2 :(得分:19)

make function仅分配和初始化slice,map或chan类型的对象。像new一样,第一个参数是一个类型。但是,它也可以采取第二个论点,即大小。与new不同,make的返回类型与其参数的类型相同,而不是指向它的指针。并且初始化分配的值(不像新的那样设置为零值)。 原因是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

答案 3 :(得分:8)

Effective Go中解释的所有内容外,new(T)&T{}之间的主要区别在于后者显式执行堆分配。但是应该注意,这是依赖于实现的,因此可能会有所变化。

makenew进行比较毫无意义,因为两者执行完全不同的功能。但这在链接文章中有详细解释。

答案 4 :(得分:6)

您需要make()来创建频道和地图(和切片,但也可以从数组创建)。没有替代方法可以制作这些内容,因此您无法从词典中删除make()

至于new(),当你可以使用struct语法时,我不知道为什么你需要它。它确实有一个独特的语义含义,即“创建并返回一个结构,所有字段都初始化为零值”,这可能很有用。

答案 5 :(得分:4)

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

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

答案 6 :(得分:4)

new() 和 make() 的区别:

  • new(T) 为 T 类型的新项分配零存储并返回其地址,类型 *T 的值:它返回一个指向新分配的 T 类型零值的指针,准备使用;它适用于数组和结构等值类型;这是 相当于 &T{ }
  • make(T) 返回一个 T 类型的初始化值;它仅适用于 3 种内置引用类型:切片、贴图和通道。

换句话说,新分配; make 初始化;

enter image description here

var p *[]int = new([]int)
or
// *p == nil; with len and cap 0
p := new([]int)

这很少有用。

enter image description here

p := make([]int, 0)

我们的切片已初始化,但这里指向一个空数组。

这两个语句都不是很有用,以下是:

var v []int = make([]int, 10, 50)
// Or
v := make([]int, 10, 50)

这将分配一个包含 50 个整数的数组,然后创建一个长度为 10、容量为 50 的切片 v,指向数组的前 10 个元素。

找出 make() 和 new() 的一些规则:

  • 对于切片、贴图和通道:使用 make
  • 对于数组、结构和所有值类型:使用 new

package main
type Foo map[string]string
type Bar struct {
         s string
         i int
}
func main() {
         // OK:
         y := new(Bar)
         (*y).s = "hello"
         (*y).i = 1

         // NOT OK:
         z := make(Bar) // compile error: cannot make type Bar
         z.s = "hello"
         z.i = 1

         // OK:
         x := make(Foo)
         x["x"] = "goodbye"
         x["y"] = "world"

         // NOT OK:
         u := new(Foo)
         (*u)["x"] = "goodbye" // !!panic!!: runtime error: 
                   // assignment to entry in nil map
         (*u)["y"] = "world"
}

频道:

func main() {
    // OK:
    ch := make(chan string)
    go sendData(ch)
    go getData(ch)
    time.Sleep(1e9)

    // NOT OK:
    ch := new(chan string)
    go sendData(ch) // cannot use ch (variable of type *chan string) 
                   // as chan string value in argument to sendData
    go getData(ch)
    time.Sleep(1e9)
}

func sendData(ch chan string) {
    ch <- "Washington"
    ch <- "Tripoli"
    ch <- "London"
    ch <- "Beijing"
    ch <- "Tokio"
}

func getData(ch chan string) {
    var input string
    for {
        input = <-ch
        fmt.Printf("%s ", input)

    }
}

答案 7 :(得分:3)

  • new(T)-分配内存,并将其设置为 T 类型的零值。.
    ..即 int 0 string ""和引用类型( slice 地图 chan

    请注意,引用的类型只是指向某些基础数据结构的指针,这些数据结构不会由nil创建
    示例:如果是 slice ,则不会创建基础的 array ,因此new(T) 返回不指向任何内容的指针

  • new([]int)-为引用的数据类型(切片地图 chan )和分配内存>初始化基础数据结构

    示例:对于 slice ,将以指定的长度和容量创建基础的 array
    请记住,与C不同,数组是Go中的原始类型!


话虽这么说

  • make(T)的行为类似于复合文字语法
  • make(T)的行为类似于new()(未初始化变量时)

    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)
    }
    

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

  • 答案 8 :(得分:1)

    已经有很多好的答案,但让我解释一下 new() 和 make() 作为单独分配器的必要性。

    1. new(T) 分配给定类型 T 的未初始化归零内存,并返回一个指向该内存的指针,以便它可以使用。清零只是意味着分配的内存将具有给定类型的零值。一些 go 类型的零值是 -
      • int - 0
      • bool - 假
      • 浮动 - 0
      • 字符串 - ""
      • struct - 每个成员的零值

    当 new() 需要处理其他三种复合类型 - chan、slice 和 map 时,就会出现问题。 这三种类型本质上是特殊的,它们的底层类型不仅仅是另一种类型,而是需要初始化的状态。例如,切片的底层状态由指向内部数组存储的第一个元素的指针、确定可以访问的元素数量的长度以及随着元素数量增加而增加的容量组成。 new() 肯定无法处理此类类型的分配,因为它们需要额外的初始化步骤,这就是 make() 发挥作用的地方。

    1. make(T, args) 是专门为 chan、slice 和 map 类型制作的。它不仅分配了 chan、slice 和 map 的内部存储类型,还初始化它们的底层状态以使它们准备好使用。例如,对于一个切片,它分配内部数组存储,将指针设置为指向该数组中的第一个元素并设置长度和容量值。