Neden () veya yeni () yapmalıyım?


204

Tanıtım dokümanları arasındaki farkı anlatan birçok paragraflar adamak new()ve make()fakat pratikte, yerel kapsamında nesneleri oluşturmak ve bunları döndürebilir.

Neden bir çift bölücü kullanırsınız?

Yanıtlar:


171

Eğer yapabileceğiniz şeyler makesize başka bir şekilde yapamaz:

  • Kanal oluşturun
  • Önceden yerleştirilmiş alanı olan bir harita oluşturun
  • Önceden yerleştirilmiş alanı olan veya len! = Cap ile bir dilim oluşturun

Haklı çıkarmak biraz daha zor new. Daha kolay hale getirdiği ana şey, kompozit olmayan türlere işaretçiler oluşturmaktır. Aşağıdaki iki işlev eşdeğerdir. Kişi biraz daha özlü:

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

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

41
'Yeni'nin kanal oluşturmak için kullanılamayacağı doğrudur. Ancak benim görüşüme göre, 'yeni' ve 'make' tek bir yerleşik işleve katılırsa ne olur? Elbette, böyle bir değiştirme mümkün olacaktır. Mümkün olduğu için, soru şudur: sadece 1 genelleştirilmiş yerleşik işlev yerine 2 yerleşik işleve sahip olmanın nesnel nedenleri nelerdir? - Cevabınız doğru bir şekilde 'yeni'nin kanallar / haritalar / dilimler oluşturmak için kullanılamayacağını söylüyor, ancak Go'nun neden 1 genel ayrılmış + başlatma işlevine sahip olmak yerine' yeni 've' make ' olduğuna dair gerekçeler sunmuyor .

5
Bir araya getirilebilirler ve hatta bir noktada Rob Pike tarafından önerilmiştir: groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion . Sonuçta cevabınızda verilenlere benzer sebeplerden dolayı olmadı.
Evan Shaw

12
Etkili git, sıfırın sıfır değerini döndürdüğü noktayı belirtirken, harita sıfırlanmamış türlere harita, dilim veya kanal tahsis eder. Bkz. Golang.org/doc/effective_go.html#allocation_new
kristianp

Bunun m := map[string]int{}yerine ne olacak m := make(map[string]int)? boyutu da önceden konumlandırmaya gerek yok.
Noam Manos

165

Go'nun birden fazla bellek ayırma ve değer başlatma yöntemi vardır:

&T{...}, &someLocalVar, new,make

Tahsis, bileşik değişmez değerler oluştururken de olabilir.


newtamsayı gibi değerleri ayırmak için kullanılabilir &int, yasadışıdır:

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

Arasındaki fark newve makeaşağıdaki örneğe bakarak görülebilir:

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

Farz edelim ki Go yoktur newve makeyerleşik fonksiyona sahiptir NEW. Sonra örnek kod şöyle görünecektir:

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

* Zorunlu olacaktır böylece:

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

Evet, birleştirme newve maketek bir yerleşik fonksiyonda mümkündür. Ancak, tek bir yerleşik işlevin, yeni Go programcıları arasında iki yerleşik işleve sahip olmaktan daha fazla karışıklığa yol açması muhtemeldir.

Yukarıdaki noktaların tümü göz önüne alındığında, ayrı kalmak newve makekalmak daha uygun görünmektedir .


@TorstenBronger Okuması daha kolay yeni buluyorum ve intbunun oluşturulduğu örnek olduğunu gösteriyor .
Daniel Toebe

4
Eğer yazma istediniz make(Point)ve make(int)bu son 2 hatlarında?
Jimmy Huch

27

makeişlevi yalnızca dilim, harita veya chan türündeki bir nesneyi ayırır ve başlatır. Gibi new, ilk argüman türüdür. Ancak, ikinci bir argüman da alabilir. Yeni öğeden farklı olarak, make'in dönüş türü, bir işaretçi değil, bağımsız değişkeninin türüyle aynıdır. Ve tahsis edilen değer başlatılır (yeni gibi sıfır değerine ayarlanmamıştır). Bunun nedeni, dilim, harita ve chan'ın veri yapıları olmasıdır. Başlatılmaları gerekir, aksi takdirde kullanılamazlar. New () ve make () 'in farklı olmasının nedeni budur.

Aşağıdaki Effective Go örnekleri çok açıktır:

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
İçinde new([]int) , sadece [] int için bellek ayırır, ancak başlatılmaz nil; işaretçi hafızaya değil çünkü kullanılamaz. make([]int)kullanılabilir hale getirmek için ayırır ve başlatır, ardından adresini döndürür.
o0omycomputero0o

12
  • new(T)- Belleği ayırır ve T tipi için sıfır değerine ayarlar .
    ..o 0için int , ""için dize ve nilbaşvurulan türleri ( dilim , harita , Chan )

    Referans verilen türlerin yalnızca temel veri yapıları , oluşturulan olmaz tarafından new(T)
    durumunda: Örnek dilim , altta yatan dizisi oluşturulmaz dolayısıyla new([]int) hiçbir şey bir gösterici ile döner

  • make(T)- Referans verilen veri türleri ( dilim , harita , chan ) için bellek ayırır ve bunların temelini oluşturan veri yapılarını başlatır

    Örnek: dilim durumunda , alttaki dizi belirtilen uzunluk ve kapasitede oluşturulacaktır.
    C'den farklı olarak, bir dizinin Go!


Söyleniyor ki:

  • make(T) kompozit-literal sözdizimi gibi davranır
  • new(T)gibi davranır var(değişken başlatılmadığında)

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

    Programı çalıştır

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

    Daha fazla okuma:
    https://golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make


  • bir örnekle işler daha açık hale gelir. upvoted :)
    Sumit Jha

    8

    make()Kanallar ve haritalar oluşturmanız gerekir (ve dilimler, ancak bunlar dizilerden de oluşturulabilir). Bunları yapmanın alternatif bir yolu yok, bu yüzden kaldıramazsınızmake() sözlüğünüzden .

    Gelince new(), struct sözdizimini kullanabildiğinizde neden ihtiyacınız olduğunu hiçbir neden bilmiyorum . Bununla birlikte, "yararlı olabilecek tüm alanları sıfır değerine sıfırlanmış bir yapı oluşturma ve döndürme" olan benzersiz bir anlamsal anlamı vardır.


    1
    Bu yüzden yeni kaçınılmalıdır ve sadece Struct sözdizimini kullanmayı tercih
    etmelisiniz

    8

    Dışında her şeyi açıklandığı Etkili Go , ana fark arasındaki new(T)ve&T{} , ikincisinin açıkça bir yığın tahsisi gerçekleştirmesidir. Bununla birlikte, bunun uygulamaya bağlı olduğu ve bu nedenle değişikliğe tabi olabileceği unutulmamalıdır.

    Karşılaştırma makeiçin newbirbirinden tamamen farklı işlevleri yerine kadar az mantıklı. Ancak bu, bağlantılı makalede ayrıntılı olarak açıklanmaktadır.


    10
    &T{}Açıkça bir yığın tahsisi gerçekleştiren iddia , spesifikasyonlardaki herhangi bir şeye dayanmayan AFAIK'tır. Aslında kaçış analizinin mümkün olduğunca böylesi bir * T yığını üzerinde tuttuğuna inanıyorum new(T).
    zzzz

    6

    yeni (T): T * T türünde bir değer yazmak için bir işaretçi döndürür , belleği ayırır ve sıfırlar. yeni (T) & T {} ile eşdeğerdir .

    make (T): T türünün başlangıç ​​değeri döndürür , Belleği ayırır ve başlatır. Dilimler, harita ve kanallar için kullanılır.

    Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
    Licensed under cc by-sa 3.0 with attribution required.