Во вводных документах много абзацев, посвященных объяснению различий между 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