Tại sao tôi sẽ làm () hoặc mới ()?


203

Các tài liệu giới thiệu dành nhiều đoạn để giải thích sự khác biệt giữa new()make(), nhưng trong thực tế, bạn có thể tạo các đối tượng trong phạm vi cục bộ và trả lại chúng.

Tại sao bạn sẽ sử dụng cặp phân bổ?

Câu trả lời:


170

Những điều bạn có thể làm với makeđiều đó bạn không thể làm theo bất kỳ cách nào khác:

  • Tạo một kênh
  • Tạo một bản đồ với không gian preallocated
  • Tạo một lát cắt với không gian preallocated hoặc với len! = Cap

Khó hơn một chút để biện minh new. Điều chính nó làm cho dễ dàng hơn là tạo con trỏ đến các loại không tổng hợp. Hai chức năng dưới đây là tương đương. Một cách ngắn gọn hơn một chút:

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

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

41
Đúng là 'mới' không thể được sử dụng để tạo kênh. Tuy nhiên, theo tôi, vấn đề là: điều gì sẽ xảy ra nếu 'mới' và 'tạo' được kết hợp thành một chức năng tích hợp duy nhất? Chắc chắn, một sự thay thế như vậy sẽ có thể. Vì có thể, câu hỏi đặt ra là: lý do khách quan nào để có 2 chức năng tích hợp thay vì chỉ có 1 chức năng tích hợp tổng quát. - Câu trả lời của bạn nói chính xác rằng 'mới' không thể được sử dụng để tạo kênh / bản đồ / lát, nhưng nó không cung cấp lý do tại sao Go có 'mới' và 'tạo', thay vì có 1 hàm cấp phát + init tổng quát.

5
Chúng có thể được kết hợp và thậm chí nó đã được đề xuất bởi Rob Pike tại một thời điểm: Groups.google.com/d/topic/golang-nuts/kWXYU95XN04/discussion . Cuối cùng, nó đã không được thông qua vì những lý do tương tự như những lý do được đưa ra trong câu trả lời của bạn.
Evan Shaw

12
Đi hiệu quả làm cho điểm mới trả về giá trị 0, trong khi bản đồ phân bổ các loại bản đồ, lát hoặc kênh khác không. Xem golang.org/doc/effective_go.html#allocation_new
kristianp

Những gì về m := map[string]int{}thay vì m := make(map[string]int)? không cần kích thước preallocate là tốt.
Noam Manos

165

Go có nhiều cách phân bổ bộ nhớ và khởi tạo giá trị:

&T{...}, &someLocalVar, new,make

Phân bổ cũng có thể xảy ra khi tạo ra chữ hỗn hợp.


newcó thể được sử dụng để phân bổ các giá trị như số nguyên, &intlà bất hợp pháp:

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

Sự khác biệt giữa newmakecó thể được nhìn thấy bằng cách xem ví dụ sau:

p := new(chan int)   // p has type: *chan int
c := make(chan int)  // c has type: chan int

Giả sử Go không có newmake, nhưng nó có chức năng tích hợp NEW. Sau đó, mã ví dụ sẽ trông như thế này:

p := NEW(*chan int)  // * is mandatory
c := NEW(chan int)

Điều * này là bắt buộc , vì vậy:

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

Có, hợp nhất newmakethành một chức năng tích hợp duy nhất là có thể. Tuy nhiên, có thể một chức năng tích hợp đơn sẽ dẫn đến sự nhầm lẫn giữa các lập trình viên Go mới hơn là có hai chức năng tích hợp sẵn.

Xem xét tất cả các điểm trên, nó có vẻ phù hợp hơn newmakevẫn riêng biệt.


@TorstenBronger Tôi thấy mới để dễ đọc hơn và cho thấy đó là trường hợp intđược tạo.
Daniel Toebe

4
Ý của bạn là viết make(Point)make(int)trong 2 dòng cuối cùng?
Jimmy Huch

27

makeHàm phân bổ và khởi tạo một đối tượng kiểu lát, bản đồ hoặc chỉ chan. Giống như new, đối số đầu tiên là một loại. Nhưng, nó cũng có thể có một đối số thứ hai, kích thước. Không giống như mới, kiểu trả về của make giống như kiểu đối số của nó, không phải là con trỏ tới nó. Và giá trị được phân bổ được khởi tạo (không được đặt thành giá trị 0 như mới). Lý do là lát, bản đồ và chan là cấu trúc dữ liệu. Chúng cần được khởi tạo, nếu không chúng sẽ không thể sử dụng được.Đây là lý do mới () và make () cần phải khác nhau.

Các ví dụ sau đây từ Go Go làm cho nó rất rõ ràng:

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
Trong new([]int)đó, nó chỉ cấp phát bộ nhớ cho [] int, nhưng không khởi tạo để nó chỉ trả về nil; không phải là con trỏ đến bộ nhớ vì nó không sử dụng được make([]int)phân bổ và khởi tạo để nó có thể sử dụng được, sau đó trả lại địa chỉ của nó.
o0omycomputero0o

12
  • new(T)- Phân bổ bộ nhớ và đặt nó thành giá trị 0 cho loại T ..
    .. đó là 0cho int , ""cho chuỗinilcho các loại tham chiếu ( lát , bản đồ , chan )

    Lưu ý rằng các kiểu được tham chiếu chỉ là con trỏ tới một số cấu trúc dữ liệu cơ bản , sẽ không được tạo bởi new(T)
    Ví dụ: trong trường hợp lát , mảng bên dưới sẽ không được tạo, do đó new([]int) trả về một con trỏ không có gì

  • make(T)- Phân bổ bộ nhớ cho các loại dữ liệu được tham chiếu ( lát , bản đồ , chan ), cộng với khởi tạo cấu trúc dữ liệu cơ bản của chúng

    Ví dụ: trong trường hợp lát , mảng bên dưới sẽ được tạo với độ dài và dung lượng đã chỉ định
    Hãy nhớ rằng, không giống như C, một mảng là kiểu nguyên thủy trong Go!


Điều đó đang được nói:

  • make(T) hành xử như cú pháp hỗn hợp
  • new(T)hành xử như var(khi biến không được khởi tạo)

    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)
    }

    Chạy chương trình

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

    Đọc thêm:
    https://golang.org/doc/effective_go.html#allocation_new https://golang.org/doc/effective_go.html#allocation_make


  • mọi thứ trở nên rõ ràng hơn với một ví dụ. nâng cấp :)
    Sumit Jha

    8

    Bạn cần make()tạo các kênh và bản đồ (và các lát, nhưng chúng cũng có thể được tạo từ các mảng). Không có cách nào khác để tạo ra chúng, vì vậy bạn không thể xóamake() khỏi từ vựng của mình.

    Về phần new()tôi không biết lý do nào khiến bạn cần nó khi bạn có thể sử dụng cú pháp struct. Mặc dù vậy, nó có một ý nghĩa ngữ nghĩa duy nhất, đó là "tạo và trả về một cấu trúc với tất cả các trường được khởi tạo thành giá trị 0" của chúng, có thể hữu ích.


    1
    Vì vậy, nên tránh mới và chỉ thích sử dụng cú pháp Struct
    CommonSenseCode

    8

    Ngoài mọi thứ được giải thích trong Go hiệu quả , Sự khác biệt chính giữa new(T)&T{}là cái sau thực hiện rõ ràng việc phân bổ heap. Tuy nhiên, cần lưu ý rằng điều này phụ thuộc vào việc thực hiện và do đó có thể thay đổi.

    So sánh makeđể newlàm cho ít ý nghĩa khi hai thực hiện các chức năng hoàn toàn khác nhau. Nhưng điều này được giải thích chi tiết trong bài viết liên kết.


    10
    Khiếu nại &T{}thực hiện phân bổ heap rõ ràng là AFAIK không dựa trên bất kỳ điều gì trong thông số kỹ thuật. Trên thực tế tôi tin rằng phân tích thoát đã giữ * T trên stack bất cứ khi nào có thể theo cùng một cách chính xác như với new(T).
    zzzz

    6

    new (T): nó trả về một con trỏ để gõ T một giá trị của loại * T, nó cấp phát và thay đổi bộ nhớ. mới (T) tương đương với & T {} .

    make (T): nó trả về giá trị khởi tạo của loại T , Nó cấp phát và khởi tạo bộ nhớ. Nó được sử dụng cho lát, bản đồ và các kênh.

    Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
    Licensed under cc by-sa 3.0 with attribution required.