Ответы:
То, что вы можете сделать с 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)в этих последних двух строках?
makeФункция выделяет и инициализирует объект типа slice, map или chan only. Мол new, первый аргумент - это тип. Но он также может принимать второй аргумент, размер. В отличие от 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для ссылочных типов ( slice , map , 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(), я не знаю ни одной причины, почему она вам нужна, когда вы можете использовать структурный синтаксис. Тем не менее, он имеет уникальное семантическое значение: «создать и вернуть структуру со всеми полями, инициализированными до их нулевого значения», что может быть полезно.
Помимо всего, что объясняется в Effective Go , основное различие между new(T)и&T{} заключается в том, что последний явно выполняет выделение кучи. Однако следует отметить, что это зависит от реализации и, следовательно, может быть изменено.
Сравнивать makeс не newимеет смысла, поскольку эти два выполняют совершенно разные функции. Но это подробно объясняется в связанной статье.
&T{}явно выполняет распределение кучи, является AFAIK, не основанным ни на чем в спецификациях. На самом деле, я считаю, что анализ escape уже хранит такие * T в стеке, когда это возможно, точно так же, как и с new(T).