3 回答
TA贡献1798条经验 获得超7个赞
当你声明一个变量时, whereT是某种类型:
var name T
Go 给你一块未初始化的“归零”内存。
对于原语,这意味着var name int它将是 0,并且var name string将是“”。在C 中,它可能被归零,或者可能是出乎意料的。Go 保证未初始化的变量是该类型的零等价物。
在内部,切片、映射和通道被视为指针。指针零值为零,意味着它指向零内存。如果没有初始化它,如果您尝试对其进行操作,您可能会遇到恐慌。
该make函数是专门为切片、贴图或通道设计的。make 函数的参数是:
make(T type, length int[, capacity int]) // For slices.
make(T[, capacity int]) // For a map.
make(T[, bufferSize int]) // For a channel. How many items can you take without blocking?
切片length是它以多少个项目开头。容量是在需要调整大小之前分配的内存(内部,新大小 * 2,然后复制)。有关更多信息,请参阅Effective Go:使用 make 分配。
结构:new(T)等价于&T{},而不是T{}。*new(T)相当于*&T{}。
切片:make([]T,0)相当于[]T{}.
地图:make(map[T]T)相当于map[T]T{}.
至于首选哪种方法,我问自己以下问题:
我现在知道函数内的值吗?
如果答案是“是”,那么我会选择上述之一T{...}。如果答案是“否”,那么我使用 make 或 new。
例如,我会避免这样的事情:
type Name struct {
FirstName string
LastName string
}
func main() {
name := &Name{FirstName:"John"}
// other code...
name.LastName = "Doe"
}
相反,我会做这样的事情:
func main() {
name := new(Name)
name.FirstName = "John"
// other code...
name.LastName = "Doe"
}
为什么?因为通过使用new(Name)我明确表示我打算稍后填充这些值。如果我使用&Name{...}它就不清楚我打算稍后在同一函数中添加/更改值而不阅读其余代码。
当您不需要指针时,结构是例外。我将使用T{},但如果我打算添加/更改值,我不会在其中添加任何内容。当然*new(T)也有效,但这就像使用*&T{}. T{}在这种情况下更干净,尽管我倾向于使用带有结构的指针来避免在传递时进行复制。
另一件要记住的事情是, a[]*struct比 更小,调整大小更便宜[]struct,假设结构比指针大得多,指针通常为 4 - 8 个字节(64 位上为 8 个字节?)。
TA贡献1864条经验 获得超2个赞
您可以查看 Go 标准库源,在那里您可以找到许多惯用的 Go 代码。
您是对的:var xs []int
与其他两个变体不同,因为它不“初始化”xs,xs 为零。而另外两个确实构造了一个切片。xs := []int{}
如果您需要一个零上限的空切片,则很常见,同时make
为您提供更多选择:长度和容量。另一方面,通常从一个 nil 切片开始并通过附加来填充var s []int; for ... { s = append(s, num) }
。
new
完全无法避免,因为它是创建指针的唯一方法,例如指向 uint32 或其他内置类型。但你是对的,写作a := new(A)
是非常罕见的,主要是a := &A{}
因为这可以变成a := &A{n: 17, whatever: "foo"}
. 使用 ofnew
并不是真的不鼓励,但考虑到结构文字的能力,对我来说它只是 Java 的遗留物。
- 3 回答
- 0 关注
- 255 浏览
添加回答
举报