Go struct方法允许类型混合?

时间:2018-03-13 08:38:32

标签: go

我有一个简单的结构,只有一个方法:

type Person struct {
  name string
}

func (p Person) SetName(name string) {
  p.name = name
}

以下输出:

dave := Person{}
dave.SetName("Dave")
fmt.Println(dave.name)

将为空,因为方法接收器接受一个值(或者更准确地创建您传递的值的副本),因此它不会修改您的基础值。

如果我将方法更改为:

func (p *Person) SetName(name string) {
  p.name = name
}

输出将是" Dave"。

现在我不明白的是我不应该在指针上调用方法吗? 因此,当初始化我的对象时,我应该这样做:

dave := &Person{}

而不是:

dave := Person{}

同样使用go反射包我试图找出以下值:

&Person

并发现它是*人,这很好。当我打印该值虽然我没有获得内存位置相比,当我打印指向int的指针的值时:

a := 4
fmt.Println(&a)

我一直在阅读文档,提出问题,但我学的越多,我就越想知道我是否遗漏了一些简单的东西,因为很多人都认为这一切都让人困惑。

1 个答案:

答案 0 :(得分:6)

您在指针接收器上调用方法并保留指向值的指针时会感到困惑。也就是说,在指针接收器上定义的调用SetName之间没有固有的连接,需要存储指向存储类型为Person的存储器的存储器{直接{1}}。

当你有声明时

Person

然后致电

var pers Person

编译器获取所占用的内存块的地址 变量pers.SetName("foo") - 就像你通过申请手工做的那样 将pers运算符设为&,并将该地址传递给pers 函数,所以调用最终完成像

SetName

或者,换句话说,(&pers).SetName("foo") 的接收器将是 SetName - 即&pers的地址。

现在没有什么可以阻止你获取pers的地址 并将其存储在其他地方:

pers

(你通常不会写这种Java风格的口吃代码,但是 我这样做是为了更清晰),现在你可以打电话了 var ptr *Person = &pers 值上的SetName

ptr

现在编译器将直接使用存储在变量ptr.SetName("bar") 中的值 将其作为方法的接收者传递。

这个"拼图的最后一点"是你用的时候 Go-applied地址获取操作符ptr提供的特殊语法 直接到文字,编译器做这样的事情:

&

...并使var __pers Person = Person{ // The contents of your literal } var dave = &__pers 变量无法按名称访问(因为它很自然 没有名字。)

正如您所看到的,这与获取指针没有什么不同 用手记住__pers值的记忆。

您可能需要咨询this FAQ entry以获得更多理解 方法如何设置类型PersonT相关联,以及后者为什么 总是包括前者,但反之亦然。 另请阅读this

更新:解释*T格式和输出方式的差异 类型fmt.Println*int的值。

  • 前者打印为base-16字符串表示 包含*Person值的内存位置的地址。
  • 使用类似的字符串表示来渲染后者 Go用于获取指向文字的指针的语法:int

此行为符合记录:&{}fmt.Print 对它们的参数使用所谓的默认格式规则 传递,这些规则取决于参数的类型。

让我们先查看fmt.Println上的文档 (运行fmt.Println):

  

go doc fmt.Println

     

func Println(a ...interface{}) (n int, err error)格式使用其操作数的默认格式   并写入标准输出。   始终在操作数之间添加空格,并附加换行符。   它返回写入的字节数和遇到的任何写入错误。

(强调我的。)

现在让我们转到Println包本身的文档 (运行fmt):

  

< ...>

     

打印

     

动词:

     

一般:

     
      
  • go doc fmt默认格式的值

         

    打印结构时,加号标记(%v)会添加字段名称。

  •   
     

< ...>

     

%+v的默认格式为:

%v
     

对于复合对象,使用这些规则打印元素,   递归地,这样布局:

bool:                    %t
int, int8 etc.:          %d
uint, uint8 etc.:        %d, %#x if printed with %#v
float32, complex64, etc: %g
string:                  %s
chan:                    %p
pointer:                 %p

struct: {field0 field1 ...} array, slice: [elem0 elem1 ...] maps: map[key1:value1 key2:value2] pointer to above: &{}, &[], &map[] 是指向结构类型的指针,因此它呈现为

*Person

其中&{name} 是name字段的内容;比方说,如果它被分配了 字符串" Dave",输出为name