Jawaban:
Hal-hal yang dapat Anda lakukan dengan make
yang tidak dapat Anda lakukan dengan cara lain:
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
}
m := map[string]int{}
sebaliknya m := make(map[string]int)
? tidak perlu mengalokasikan ukuran juga.
Go memiliki banyak cara alokasi memori dan inisialisasi nilai:
&T{...}
, &someLocalVar
, new
,make
Alokasi juga dapat terjadi saat membuat literal komposit.
new
dapat digunakan untuk mengalokasikan nilai-nilai seperti bilangan bulat, &int
ilegal:
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 new
dan make
dapat 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 new
dan 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 new
dan make
menjadi 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 new
dan make
tetap terpisah.
int
dibuat.
make(Point)
dan make(int)
dalam 2 baris terakhir?
make
fungsi 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
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.
new(T)
- Mengalokasikan memori, dan menetapkannya ke nilai nol untuk jenis T ..
..that adalah 0
untuk int , ""
untuk tali dan nil
untuk 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
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.
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 make
dengan new
sedikit masuk akal karena keduanya melakukan fungsi yang sama sekali berbeda. Tetapi ini dijelaskan secara rinci dalam artikel yang ditautkan.
&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)
.