Câu trả lời:
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:
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
}
m := map[string]int{}
thay vì m := make(map[string]int)
? không cần kích thước preallocate là tốt.
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.
new
có thể được sử dụng để phân bổ các giá trị như số nguyên, &int
là 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 new
và make
có 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ó new
và make
, 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 new
và make
thà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 new
và make
vẫn riêng biệt.
int
được tạo.
make(Point)
và make(int)
trong 2 dòng cuối cùng?
make
Hà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
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ó.
new(T)
- Phân bổ bộ nhớ và đặt nó thành giá trị 0 cho loại T ..
.. đó là 0
cho int , ""
cho chuỗi và nil
cho 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
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.
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)
và &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
để new
là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.
&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)
.