Mengapa saya membuat () atau baru ()?

203

Dokumen pengantar mendedikasikan banyak paragraf untuk menjelaskan perbedaan di antara keduanya new() danmake() , tetapi dalam praktiknya, Anda dapat membuat objek dalam cakupan lokal dan mengembalikannya.

Mengapa Anda menggunakan pasangan pengalokasi?

slezica
sumber

Jawaban:

170

Hal-hal yang dapat Anda lakukan dengan makeyang tidak dapat Anda lakukan dengan cara lain:

  • Buat saluran
  • Buat peta dengan ruang yang telah dialokasikan sebelumnya
  • Buat slice dengan spasi yang telah dialokasikan sebelumnya atau dengan len! = Cap

Agak sulit untuk dibenarkan new. Hal utama yang membuatnya lebih mudah adalah membuat pointer ke tipe non-komposit. Dua fungsi di bawah ini setara. Satu hanya sedikit lebih ringkas:

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

func newInt2() *int {
    var i int
    return &i
}
Evan Shaw
sumber
41
Benar bahwa 'baru' tidak dapat digunakan untuk membuat saluran. Namun, menurut saya, intinya adalah: apa yang akan terjadi jika 'baru' dan 'make' digabungkan menjadi satu fungsi bawaan? Tentu saja, penggantian seperti itu dimungkinkan. Karena dimungkinkan, pertanyaannya adalah: apa alasan obyektif untuk memiliki 2 fungsi built-in daripada hanya 1 fungsi built-in umum. - Jawaban Anda dengan benar mengatakan bahwa 'baru' tidak dapat digunakan untuk membuat saluran / peta / irisan, tetapi itu tidak memberikan alasan mengapa Go memiliki 'baru' dan 'membuat', alih-alih memiliki 1 fungsi alokasi + init umum.
5
Mereka dapat digabungkan dan bahkan diusulkan oleh Rob Pike pada satu titik: groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion . Pada akhirnya itu tidak melalui alasan yang mirip dengan yang diberikan dalam jawaban Anda.
Evan Shaw
12
Go efektif membuat titik bahwa baru mengembalikan nilai nol, sedangkan peta mengalokasikan peta jenis non-zeroed, irisan atau saluran. Lihat golang.org/doc/effective_go.html#allocation_new
kristianp
Bagaimana dengan m := map[string]int{}sebaliknya m := make(map[string]int)? tidak perlu mengalokasikan ukuran juga.
Noam Manos
165

Go memiliki banyak cara alokasi memori dan inisialisasi nilai:

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

Alokasi juga dapat terjadi saat membuat literal komposit.


newdapat digunakan untuk mengalokasikan nilai-nilai seperti bilangan bulat, &intilegal:

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

Perbedaan antara newdan makedapat dilihat dengan melihat contoh berikut:

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

Misalkan Go tidak memiliki newdan make, tetapi memiliki fungsi bawaan NEW. Maka kode contoh akan terlihat seperti ini:

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

Itu * wajib , jadi:

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

Ya, menggabungkan newdan makemenjadi satu fungsi bawaan dimungkinkan. Namun, ada kemungkinan bahwa fungsi built-in tunggal akan menyebabkan lebih banyak kebingungan di antara programmer Go daripada memiliki dua fungsi built-in.

Mempertimbangkan semua poin di atas, tampaknya lebih tepat untuk newdan maketetap terpisah.


sumber
@ ThorstenBronger Saya menemukan yang baru agar lebih mudah dibaca dan menunjukkan bahwa itu adalah contoh di mana intdibuat.
Daniel Toebe
4
Apakah Anda bermaksud menulis make(Point)dan make(int)dalam 2 baris terakhir?
Jimmy Huch
27

makefungsi mengalokasikan dan menginisialisasi objek dengan tipe slice, map, atau chan only. Seperti new, argumen pertama adalah tipe. Tapi, bisa juga argumen kedua, ukuran. Tidak seperti yang baru, tipe return make sama dengan tipe argumennya, bukan pointer. Dan nilai yang dialokasikan diinisialisasi (tidak diatur ke nilai nol seperti yang baru). Alasannya adalah bahwa slice, map dan chan adalah struktur data. Mereka perlu diinisialisasi, kalau tidak mereka tidak akan dapat digunakan. Inilah alasan baru () dan make () harus berbeda.

Contoh-contoh berikut dari Effective Go membuatnya sangat jelas:

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
Sinatra
sumber
1
Di new([]int), itu hanya mengalokasikan memori untuk [] int, tetapi tidak menginisialisasi sehingga hanya mengembalikan nil; bukan penunjuk ke memori karena tidak dapat digunakan. make([]int)mengalokasikan dan menginisialisasi sehingga dapat digunakan, lalu kembalikan alamatnya.
o0omycomputero0o
12
  • new(T)- Mengalokasikan memori, dan menetapkannya ke nilai nol untuk jenis T ..
    ..that adalah 0untuk int , ""untuk tali dan niluntuk jenis direferensikan ( slice , peta , chan )

    Perhatikan bahwa tipe yang direferensikan hanyalah pointer ke beberapa struktur data yang mendasarinya , yang tidak akan dibuat oleh new(T)
    Contoh: dalam kasus slice , array yang mendasarinya tidak akan dibuat, sehingga new([]int) mengembalikan pointer ke nol.

  • make(T)- memori Alokasikan untuk jenis direferensikan data ( slice , peta , chan ), ditambah menginisialisasi mereka struktur data yang mendasari

    Contoh: dalam kasus slice , array yang mendasarinya akan dibuat dengan panjang dan kapasitas yang ditentukan.
    Ingatlah, tidak seperti C, array adalah tipe primitif di Go!


Yang telah dibilang:

  • make(T) berperilaku seperti sintaks komposit-literal
  • new(T)berperilaku seperti var(ketika variabel tidak diinisialisasi)

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

    Jalankan programnya

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

    Bacaan lebih lanjut:
    https://golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make

  • Loris
    sumber
    segalanya menjadi lebih jelas dengan sebuah contoh. upvoted :)
    Sumit Jha
    8

    Anda perlu make()membuat saluran dan peta (dan irisan, tetapi itu juga dapat dibuat dari array). Tidak ada cara alternatif untuk membuatnya, jadi Anda tidak bisa menghapusnyamake() dari leksikon Anda.

    Adapun new(), saya tidak tahu alasan apa pun begitu saja mengapa Anda membutuhkannya ketika Anda dapat menggunakan sintaks struct. Itu memang memiliki makna semantik yang unik, yaitu "membuat dan mengembalikan struct dengan semua bidang diinisialisasi ke nilai nol", yang dapat berguna.

    Lily Ballard
    sumber
    1
    Jadi baru harus dihindari dan hanya lebih suka menggunakan sintaks Struct
    CommonSenseCode
    8

    Terlepas dari semua yang dijelaskan dalam Efektif Go , Perbedaan utama antara new(T)dan &T{}adalah bahwa yang terakhir secara eksplisit melakukan alokasi tumpukan. Namun perlu dicatat bahwa ini tergantung pada implementasi dan karenanya dapat berubah.

    Dibandingkan makedengan newsedikit masuk akal karena keduanya melakukan fungsi yang sama sekali berbeda. Tetapi ini dijelaskan secara rinci dalam artikel yang ditautkan.

    Jim
    sumber
    10
    Klaim yang &T{}secara eksplisit melakukan alokasi tumpukan adalah AFAIK tidak didasarkan pada apa pun dalam spesifikasi. Sebenarnya saya percaya analisis melarikan diri sudah menyimpan * T pada stack bila memungkinkan dengan cara yang sama persis dengan new(T).
    zzzz
    6

    new (T): mengembalikan pointer ke tipe T nilai tipe * T, mengalokasikan dan nol memori. baru (T) setara dengan & T {} .

    make (T): mengembalikan nilai yang diinisialisasi dari tipe T , Ini mengalokasikan dan menginisialisasi memori. Ini digunakan untuk irisan, peta dan saluran.

    M.Nair
    sumber