Відповіді:
Що ви можете зробити з 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).