介绍文档用了很多段落来解释new()和make()之间的区别,但实际上,您可以在局部范围内创建对象并返回它们。
为什么要使用这对分配器?
介绍文档用了很多段落来解释new()和make()之间的区别,但实际上,您可以在局部范围内创建对象并返回它们。
为什么要使用这对分配器?
当前回答
new()和make()的区别:
new(T)为一个类型为T的新项分配零存储,并返回它的地址,一个类型为*T的值:它返回一个指向新分配的类型为T的零值的指针,准备使用;它适用于数组和结构等值类型;它是 等价于&T{} make(T)返回一个初始化的T类型值;它只适用于3种内置的引用类型:切片、映射和通道。
换句话说,新的分配;使初始化;
var p *[]int = new([]int)
or
// *p == nil; with len and cap 0
p := new([]int)
这很少有用。
p := make([]int, 0)
我们的切片已经初始化,但这里指向一个空数组。
这两种说法都不是很有用,下面是:
var v []int = make([]int, 10, 50)
// Or
v := make([]int, 10, 50)
这将分配一个50个整型数组,然后创建一个长度为10,容量为50的切片v,指向数组的前10个元素。
找出make()和new()的一些规则:
对于切片、映射和通道:使用make 对于数组、结构和所有值类型:使用new
package main
type Foo map[string]string
type Bar struct {
s string
i int
}
func main() {
// OK:
y := new(Bar)
(*y).s = "hello"
(*y).i = 1
// NOT OK:
z := make(Bar) // compile error: cannot make type Bar
z.s = "hello"
z.i = 1
// OK:
x := make(Foo)
x["x"] = "goodbye"
x["y"] = "world"
// NOT OK:
u := new(Foo)
(*u)["x"] = "goodbye" // !!panic!!: runtime error:
// assignment to entry in nil map
(*u)["y"] = "world"
}
渠道:
func main() {
// OK:
ch := make(chan string)
go sendData(ch)
go getData(ch)
time.Sleep(1e9)
// NOT OK:
ch := new(chan string)
go sendData(ch) // cannot use ch (variable of type *chan string)
// as chan string value in argument to sendData
go getData(ch)
time.Sleep(1e9)
}
func sendData(ch chan string) {
ch <- "Washington"
ch <- "Tripoli"
ch <- "London"
ch <- "Beijing"
ch <- "Tokio"
}
func getData(ch chan string) {
var input string
for {
input = <-ch
fmt.Printf("%s ", input)
}
}
其他回答
您需要make()来创建通道和映射(以及切片,但这些也可以从数组创建)。没有其他方法来创建这些,所以不能从词典中删除make()。
至于new(),当可以使用结构语法时,我不知道为什么还需要它。但它确实有一个独特的语义含义,即“创建并返回一个将所有字段初始化为零值的结构体”,这很有用。
new()和make()的区别:
new(T)为一个类型为T的新项分配零存储,并返回它的地址,一个类型为*T的值:它返回一个指向新分配的类型为T的零值的指针,准备使用;它适用于数组和结构等值类型;它是 等价于&T{} make(T)返回一个初始化的T类型值;它只适用于3种内置的引用类型:切片、映射和通道。
换句话说,新的分配;使初始化;
var p *[]int = new([]int)
or
// *p == nil; with len and cap 0
p := new([]int)
这很少有用。
p := make([]int, 0)
我们的切片已经初始化,但这里指向一个空数组。
这两种说法都不是很有用,下面是:
var v []int = make([]int, 10, 50)
// Or
v := make([]int, 10, 50)
这将分配一个50个整型数组,然后创建一个长度为10,容量为50的切片v,指向数组的前10个元素。
找出make()和new()的一些规则:
对于切片、映射和通道:使用make 对于数组、结构和所有值类型:使用new
package main
type Foo map[string]string
type Bar struct {
s string
i int
}
func main() {
// OK:
y := new(Bar)
(*y).s = "hello"
(*y).i = 1
// NOT OK:
z := make(Bar) // compile error: cannot make type Bar
z.s = "hello"
z.i = 1
// OK:
x := make(Foo)
x["x"] = "goodbye"
x["y"] = "world"
// NOT OK:
u := new(Foo)
(*u)["x"] = "goodbye" // !!panic!!: runtime error:
// assignment to entry in nil map
(*u)["y"] = "world"
}
渠道:
func main() {
// OK:
ch := make(chan string)
go sendData(ch)
go getData(ch)
time.Sleep(1e9)
// NOT OK:
ch := new(chan string)
go sendData(ch) // cannot use ch (variable of type *chan string)
// as chan string value in argument to sendData
go getData(ch)
time.Sleep(1e9)
}
func sendData(ch chan string) {
ch <- "Washington"
ch <- "Tripoli"
ch <- "London"
ch <- "Beijing"
ch <- "Tokio"
}
func getData(ch chan string) {
var input string
for {
input = <-ch
fmt.Printf("%s ", input)
}
}
“make”的好处在其他答案中有很多,但是“New”比上面没有提到的make有一个额外的好处:泛型(截至1.18)。
假设你有一组平面(所有字段都是原语)结构体,如下所示:
type SomeStruct struct {
V1 string `json:"v1"`
V2 string `json:"v2"`
}
你想要创建一个映射函数,将一个map[string]字符串转换为任何结构体。然后你可以这样写:
func GetStructFromMap[T any](values map[string]string) (T, error) {
myStr := T{}
bytes, err := json.Marshal(values)
if err != nil {
return *myStr, err
}
if err := json.Unmarshal(bytes, str); err != nil {
return *myStr, err
}
return *myStr, nil
}
但是,这段代码将抛出一个关于myStr:= T{}行的错误,关于无效的组合值。用myStr:= make(T)替换它会产生另一个关于没有底层类型的错误。因此,您需要将该行替换为myStr:= new(T),这将创建一个对该结构的零值实例的引用。
可以看到,在处理泛型时,new可以用来实例化编译时未知的类型。
另一方面,在这个特定的示例中还可以使用命名返回类型,但更普遍的用法仍然有效。
new(T) - Allocates memory, and sets it to the zero value for type T.. ..that is 0 for int, "" for string and nil for referenced types (slice, map, chan) Note that referenced types are just pointers to some underlying data structures, which won't be created by new(T) Example: in case of slice, the underlying array won't be created, thus new([]int) returns a pointer to nothing make(T) - Allocates memory for referenced data types (slice, map, chan), plus initializes their underlying data structures Example: in case of slice, the underlying array will be created with the specified length and capacity Bear in mind that, unlike C, an array is a primitive type in 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
除了在Effective Go中解释的所有内容外,new(T)和&T{}之间的主要区别是后者显式执行堆分配。但是,应该注意的是,这取决于实现,因此可能会发生变化。
比较make和new没有什么意义,因为两者执行完全不同的功能。但这在链接的文章中有详细的解释。