GoLang中初始化和清零、以及new()和make()之间的区别

11

我来自C#背景,对于Go语言的初始化和零值定义方式感到有些困惑。我想你可以猜到这种混淆是由Go中的make()new()函数引起的。当这些方法运行时,我应该期望发生什么内部操作?初始化和零值操作会发生什么情况?

我知道在Go语言中有一个init()函数用于初始化包,但我认为这与此不同。

无论如何,它们之间有什么区别?

更新

我回答了自己的问题,请查看我的答案。

3个回答

15

我认为我已经理解了它,并决定分享一下我到目前为止所理解的内容。

make() vs. new()

我现在明白了make()new()之间的区别。起初,有点困惑,但我现在明白了:

new就像C#或Java中的new,但由于Go语言中没有构造函数,所有字段(如Java和C#术语中的字段)都将被清零。清零更像是将字段默认为零。因此,如果字段类型是int,那么它将是0,如果是struct,则将默认为nil,对于string类型,则为""。实际上,当只有无参构造函数可用且您未手动设置成员时,它与C#和Java类似。

但是,像mapslicechannel这样的类型是不同的。它们是不同的,因为它们实际上是包装器类型,它们包装了一个数组类型以在幕后保存值。因此,类似于C#和Java中的List<T>ArrayList。但在这种情况下,仅使用new是不够的,因为底层数组应该被初始化为一个空数组才能使用。因为您无法向类型为nil(或null)的数组类型的字段添加或删除内容。因此,他们提供了make()方法来帮助您初始化slices等。

那么,当您在片段中使用new()时会发生什么?简单:由于底层数组将是nil,因此片段将指向一个nil数组。

因此,new()看起来就像以下C#/Java代码:

public class Person{
   public string Name;
   public int Age;
   public Address HomeAddress;
}

var person = new Person();
Console.WriteLine(person.Name); // ""
Console.WriteLine(person.Age); // 0
Console.WriteLine(person.HomeAddress); // null

make() 对于slicemapchannel 则会像这样:

public class PersonList{
   // We are initializing the array so that we can use it.
   // Its capacity can increase.
   private Person[] _personList = new Person[100];
   public void Add(Person p){}
   public void Remove(Person p){}
   public Person Get(int index){}
}

初始化与清零

简单来说,清零是一种初始化形式。起初,我认为它们不同,但实际上它们并不是。初始化是一个更通用的术语,而如果你将结构体或变量的字段(属性等)设置为其类型的默认值,如0nil""false等,则称之为清零。然而,你可以使用组合字面量,比如hello := Hello{name="world"},这类似于C#中的var hello = new Hello() {Name = "World"},然后你就可以将你的Hello对象初始化为一个name字段设置为world

在C#中,当你说new List<string>()时,[底层数组字段会初始化为新数组],而make()则在幕后执行了类似的操作,但作为一种语言构造(内置于语言本身):

(http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646):

enter image description here

因此,new会进行清零并返回一个指针。而make()则将底层数组初始化为每个元素的默认值,并返回值本身而不是指针。


2
new 不会将任何内容清零,它只是返回一个指向该类型的指针,所有类型默认都被清零:var t Type 被清零,t := &Type{}var t Type; ptr := &tt := new(Type) 相同,但在 new 返回指针之前,该类型会被清零。 - OneOfOne
@OneOfOne:经过一段时间,你的评论开始有意义了 :) 谢谢。 - Tarik

5

但出于某种原因,我觉得这个答案还不够完整。例如,当我在结构体上使用它时会发生什么?而且不确定使用new返回指针与我的问题有什么关系?抱歉,也许我还没有理解。 - Tarik
@Tarik new 返回一个指针,这是它的功能,它相当于 &type{}。你不能在结构体上使用 make。 - OneOfOne
谢谢你的回答。它让我最终找到了答案。 - Tarik

1

new(T) 分配类型为 T 的新项的零值存储,并返回其地址,即类型为 *T 的值:它返回指向新分配的类型为 T 的零值的指针,可以直接使用;它适用于数组和结构等值类型。等同于 &T{ }

make(T) 返回类型为 T 的已初始化值;仅适用于三种内置引用类型:切片、映射和通道。

var p *[]int = new([]int) // *p == nil; with len and cap 0
//or    
p := new([]int)

var v []int = make([]int, 10, 50)
//or
v := make([]int, 10, 50)

//This allocates an array of 50 ints and then creates a slice v with length 10 and capacity 50 pointing
//to the first 10 elements of the array

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接