Golang基础知识:struct和new()关键字

57

我在学习 Golang 时,遇到了讲解结构体的章节,其中介绍了不同的结构体初始化方法。

p1 := passport{}
var p2 passport
p3 := passport{
    Photo: make([]byte, 0, 0),
    Name: "Scott",
    Surname: "Adam",
    DateOfBirth: "Some time",
}

fmt.Printf("%s\n%s\n%s\n", p1, p2, p3)

虽然这些代码会打印结构的值,如

{ } { } { Scott Adam Some time} 但下面的代码由于是引用,所以会带有一个&符号。

pointerp1 := &p3
fmt.Printf("%s", pointerp1)
pointerp2 := new(passport)
pointerp2.Name = "Anotherscott"
fmt.Printf("%s", pointerp2)

&{ Scott Adam 一些时间 }&{ 另一个Scott }

请帮我解答我的疑问。

  1. 在使用pointerp1 := &p3时,pointerp1是对p3的引用变量,它保存实际数据。同样,对于pointerp2来说,保存数据的实际变量是什么?

  2. 在哪些情况下最好使用这些不同类型的初始化?


6
Go语言没有“引用”的概念。不要将指针命名为“引用”。 - Volker
4个回答

79

new 分配一个新项目或类型的零存储,然后返回指向它的指针。我认为使用 new 或短变量声明 := type{} 并没有太大关系,这主要是个人喜好。

至于 pointer2pointer2 变量保存其自己的数据,当你执行

// initializing a zeroed 'passport in memory'
pointerp2 := new(passport)
// setting the field Name to whatever
pointerp2.Name = "Anotherscott"

new在内存中分配零化的存储空间并返回指向它的指针,因此简而言之,new将返回指向所创建对象的指针,这就是为什么pointerp2返回&{ Anotherscott }

当您需要修改传递的变量时,主要使用指针(但要注意数据竞争并使用互斥锁或通道,如果您需要从不同函数读取和写入变量)。

人们通常使用的一种替代new的方法是简短地声明一个指针类型:

blah := &passport{}

现在,blah是指向护照类型的指针。

您可以在此播放区中看到:

http://play.golang.org/p/9OuM2Kqncq

当传递指针时,您可以修改原始值。当传递非指针时,您无法修改它。这是因为在Go中,变量作为复制品传递。因此,在iDontTakeAPointer函数中,它接收测试者结构的副本,然后修改名称字段,然后返回,这对我们没有任何作用,因为它修改的是副本而不是原始值。


1
iDoTakeAPointer 函数同样会接收指针的一个副本(但它所指向的数据完全相同)。 - kopiczko
6
我认为go团队使用new作为内存分配操作是一个非常奇怪的决定,其他语言使用的是&符号。new似乎会在分配内存之后将项目复制到其中,这与实际情况相反。 - Dominic
@Dominic 完全同意,从 C++ 转过来,它看起来非常违反直觉。 - EternalObserver

5

关键字new的作用是创建一个你想要的类型的实例。然而,它不仅返回该类型的简单声明,还引用并返回该类型在程序进程堆中的实际内存地址。


4
  1. 目前有一个保存数据的变量。您可以使用*pointerp2解引用指针,甚至将其分配给变量(p2 := pointerp2),但是这个变量将是数据的副本。也就是说,修改一个不再影响另一个(http://play.golang.org/p/9yRYbyvG8q)。

  2. new往往不太受欢迎,特别是对于结构体来说更是如此。关于它的目的(提示: 它是最早出现的)和用例的讨论可以在https://softwareengineering.stackexchange.com/a/216582找到。

编辑: 此外,p1并不是与p3不同类型的初始化,而是将类型的任何字段赋值为它们的零值(""对于stringnil对于[]byte)。任何省略的字段都会发生相同的情况:

p4 := passport{
    Name: "Scott",
    Surname: "Adam",
}

在这种情况下,p4.Photop4.DateOfBirth仍然是零值(分别为nil"")。passport{}的情况只是所有字段都被省略的情况。

我知道这一点,但是为了让新手能够理解,即使他们看到[],它仍然实际上是nil。一个例子是获取JSON值时得到null:http://play.golang.org/p/GiQnKssZjH,即使在调试时你认为你有一个空切片。 - Datsik

0

我在golang中遇到了奇怪的现象,我的指针声明为myptr:= new(ptrtype),但从if myptr==nil返回的结果是false,所以我试着将其定义为myptr:=&ptrtype{},但仍然不起作用。然后我只是使用new()定义了指针,然后将其设置为nill,现在它可以工作了。不知道为什么其他方法不需要这样做。


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