Чому я б робив () або новий ()?


203

Вступні документи присвячують багато абзаців поясненню різниці між new()і make(), але на практиці ви можете створювати об'єкти в локальному масштабі та повертати їх.

Чому б ви використовували пару розподільників?

Відповіді:


170

Що ви можете зробити з makeцим, ви не можете зробити інший спосіб:

  • Створіть канал
  • Створіть карту з попередньо виділеним простором
  • Створіть фрагмент із заздалегідь виділеним пробілом або з лінією! = Cap

Це трохи важче виправдати new. Головне, що полегшує, це створення покажчиків на некомпозитні типи. Дві функції нижче є рівнозначними. Один лише трохи більш стислий:

func newInt1() *int { return new(int) }

func newInt2() *int {
    var i int
    return &i
}

41
Це правда, що "new" не можна використовувати для створення каналів. Однак, на мій погляд, справа в тому, що буде, якщо "новий" і "зробити" об'єднані в єдину вбудовану функцію? Звичайно, така заміна була б можливою. Оскільки це можливо, питання полягає в тому, які об’єктивні причини існування 2 вбудованих функцій, а не лише 1 узагальненої вбудованої функції. - Ваша відповідь правильно говорить, що "new" не можна використовувати для створення каналів / карт / фрагментів, але це не дає обгрунтування того, чому Go має "new" і "make", замість того, щоб мати 1 узагальнену функцію alloc + init.

5
Їх можна поєднувати, і це навіть було запропоновано Роб Пайком в один момент: groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion . Зрештою, це не вдалося пройти через причини, подібні до тих, що наведені у вашій відповіді.
Еван Шоу

12
Ефективний перехід означає, що новий повертає нульове значення, тоді як карта виділяє ненульові типи карту, фрагмент або канал. Дивіться golang.org/doc/effective_go.html#allocation_new
kristianp

А як щодо m := map[string]int{}цього m := make(map[string]int)? не потрібно також виділяти розмір.
Ноам Манос

165

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залишатися окремим.


@TorstenBronger Я знаходжу нове, яке простіше читати, і показує, що саме цей екземпляр intстворюється.
Даніель Тобі

4
Ви мали на увазі написати make(Point)і make(int)в цих останніх 2 рядках?
Джиммі Гуч

27

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

1
У програмі new([]int)він просто виділяє пам'ять для [] int, але не ініціалізує, тому просто повертається nil; не вказівник на пам'ять, тому що він непридатний. make([]int)виділяє та ініціалізує так, щоб це було зручно, а потім повертає його адресу.
o0omycomputero0o

12
  • 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


  • все стає більш зрозумілим на прикладі. upvoted :)
    Джа

    8

    Вам потрібно make()створити канали та карти (і фрагменти, але їх можна створити і з масивів). Немає альтернативного способу зробити їх, тому ви не можете видалити make()зі свого лексикону.

    Щодо того new(), я не знаю з будь-якої причини з того, чому вам це потрібно, коли ви можете використовувати синтаксис структури. Він має унікальне смислове значення, але це "створити та повернути структуру з усіма полями, ініціалізованими до їх нульового значення", що може бути корисним.


    1
    Тож слід уникати нового і просто віддавати перевагу використанню синтаксису Struct
    CommonSenseCode

    8

    Крім усього, що пояснено в « Ефективний перехід» , основна відмінність new(T)і &T{}полягає в тому, що остання явно виконує розподіл купи. Однак слід зазначити, що це залежить від впровадження і тому може зазнавати змін.

    За порівнянні makeз НЕ newмає сенсу , оскільки два виконують абсолютно різні функції. Але це детально пояснено у зв’язаній статті.


    10
    Претензія, яка &T{}явно виконує розподіл купи, - це AFAIK, яка не ґрунтується на чомусь специфікації. Насправді я вважаю, що аналіз втечі вже зберігає такий * T у стеці, коли це можливо, точно так само, як і з new(T).
    zzzz

    6

    new (T): він повертає вказівник на тип T значення типу * T, він виділяє і нулює пам’ять. новий (T) еквівалентний & T {} .

    make (T): повертає ініціалізоване значення типу T , воно виділяє та ініціалізує пам'ять. Його використовують для скибочок, карти та каналів.

    Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
    Licensed under cc by-sa 3.0 with attribution required.