Відповіді:
Що ви можете зробити з make
цим, ви не можете зробити інший спосіб:
Це трохи важче виправдати new
. Головне, що полегшує, це створення покажчиків на некомпозитні типи. Дві функції нижче є рівнозначними. Один лише трохи більш стислий:
func newInt1() *int { return new(int) }
func newInt2() *int {
var i int
return &i
}
m := map[string]int{}
цього m := make(map[string]int)
? не потрібно також виділяти розмір.
Go має кілька способів розподілу пам'яті та ініціалізації значень:
&T{...}
, &someLocalVar
, new
,make
Виділення можуть відбуватися і при створенні складених літералів.
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
Різницю між new
та make
можна побачити, переглянувши наступний приклад:
p := new(chan int) // p has type: *chan int
c := make(chan int) // c has type: chan int
Припустимо, у Go немає new
і 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
Так, злиття new
і make
в єдину вбудовану функцію можливо. Однак, ймовірно, що одна вбудована функція призведе до більшої плутанини серед нових програмістів Go, ніж дві вбудовані функції.
Враховуючи всі вищезазначені моменти, видається більш доцільним new
і make
залишатися окремим.
int
створюється.
make(Point)
і make(int)
в цих останніх 2 рядках?
make
Функція виділяє та ініціалізує лише об'єкт типу фрагмент, карта чи шан. Мовляв new
, перший аргумент - це тип. Але, це може також взяти другий аргумент, розмір. На відміну від нового, тип повернення make є таким же, як тип його аргументу, а не вказівник на нього. І виділене значення ініціалізується (не встановлюється до нульового значення, як у новому). Причина полягає в тому, що фрагмент, карта і чан - це структури даних. Їх потрібно ініціалізувати, інакше вони не будуть корисними. Це причина 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
new([]int)
він просто виділяє пам'ять для [] int, але не ініціалізує, тому просто повертається nil
; не вказівник на пам'ять, тому що він непридатний. make([]int)
виділяє та ініціалізує так, щоб це було зручно, а потім повертає його адресу.
new(T)
- Виділяє пам'ять і встановлює її нульове значення для типу T .. ..
це 0
для int , ""
для рядка та nil
для посилаються типів ( фрагмент , карта , chan )
Зверніть увагу , що посилальні типи просто покажчики на деякі базові структури даних , які створено не буде, по new(T)
прикладу: в разі зрізу , основний масив не буде створена, таким чином , new([]int)
повертає покажчик на нічого
make(T)
- Виділяє пам'ять для посилаються типів даних ( фрагмент , карта , чан ), а також ініціалізує їх основні структури даних
Приклад: у випадку зрізу базовий масив буде створений із заданою довжиною та ємністю
Майте на увазі, що на відміну від 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
Вам потрібно make()
створити канали та карти (і фрагменти, але їх можна створити і з масивів). Немає альтернативного способу зробити їх, тому ви не можете видалити make()
зі свого лексикону.
Щодо того new()
, я не знаю з будь-якої причини з того, чому вам це потрібно, коли ви можете використовувати синтаксис структури. Він має унікальне смислове значення, але це "створити та повернути структуру з усіма полями, ініціалізованими до їх нульового значення", що може бути корисним.
Крім усього, що пояснено в « Ефективний перехід» , основна відмінність new(T)
і &T{}
полягає в тому, що остання явно виконує розподіл купи. Однак слід зазначити, що це залежить від впровадження і тому може зазнавати змін.
За порівнянні make
з НЕ new
має сенсу , оскільки два виконують абсолютно різні функції. Але це детально пояснено у зв’язаній статті.
&T{}
явно виконує розподіл купи, - це AFAIK, яка не ґрунтується на чомусь специфікації. Насправді я вважаю, що аналіз втечі вже зберігає такий * T у стеці, коли це можливо, точно так само, як і з new(T)
.