Во вводных документах много абзацев, посвященных объяснению различий между new() и make(), но на практике вы можете создавать объекты в локальной области видимости и возвращать их.
Зачем вам использовать пару распределителей?
Во вводных документах много абзацев, посвященных объяснению различий между new() и make(), но на практике вы можете создавать объекты в локальной области видимости и возвращать их.
Зачем вам использовать пару распределителей?
Что вы можете сделать с make, что вы не можете сделать иначе:
Немного сложнее оправдать new. Главное, что упрощается, это создание указателей на некомпозитные типы.
Две приведенные ниже функции эквивалентны. Один чуть более краткий:
func newInt1() *int { return new(int) }
func newInt2() *int {
var i int
return &i
}
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 остается отдельный.
make выделяет и инициализирует только объект типа slice, map или chan. Как новый, первым аргументом является тип. Но, он также может принять второй аргумент - размер. В отличие от нового, тип возвращаемого значения аналогичен типу его аргумента, а не указателю на него. И выделенное значение инициализируется (не устанавливается на нулевое значение, как в новом). Причина в том, что срез, карта и чан являются структурами данных. Они должны быть инициализированы, иначе они не будут использоваться. Это причина, по которой 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(T) и &T{} заключается в том, что последний явно выполняет распределение кучи, Однако следует отметить, что это зависит от реализации и, следовательно, может быть изменено.
Сравнение make с new имеет мало смысла, поскольку они выполняют совершенно разные функции. Но это подробно объясняется в связанной статье.
Вам нужно make() создать каналы и карты (и срезы, но они также могут быть созданы из массивов). Нет альтернативного способа их создания, поэтому вы не можете удалить make() из своего лексикона.
Что касается new(), я не знаю какой-либо причины, почему вам это нужно, когда вы можете использовать синтаксис структуры. У этого есть уникальное семантическое значение, но это - "создать и вернуть структуру со всеми полями, инициализированными их нулевым значением", что может быть полезно.
new (T): он возвращает указатель , чтобы ввести T значение типа * T, оно выделяет и обнуляет память. новый (T) эквивалентен & T {}.
make (T): он возвращает инициализированное значение типа T, он выделяет и инициализирует память. Он используется для фрагментов, карт и каналов.
new(T) - выделяет память и устанавливает ее в нулевое значение для типа T..
.. это 0 для int, "" для строки и nil для ссылочных типов (slice, map, chan)
Обратите внимание, что ссылочные типы являются просто указателями на некоторые базовые структуры данных, которые не будут созданы new(T)
Пример: в случае слайса базовый массив не будет создан, поэтому new([]int) возвращает указатель на ничто
make(T) - выделяет память для ссылочных типов данных (slice, map, chan), а также инициализирует их базовые структуры данных
Пример: в случае среза будет создан базовый массив с указанной длиной и емкостью
Имейте в виду, что в отличие от C массив в Go является примитивным типом!
Что, как говорится:
make(T) ведет себя как составной-буквальный синтаксис new() ведет себя как 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