你如何决定你的函数是否应该使用指针作为接收器?

时间:2014-02-27 01:31:48

标签: go

考虑二叉树节点:

type Node struct {
    value uint8
    left, right *Node
}

如果我想为每个节点添加一个函数,那么它应该是:

A:  func (n *Node) height() int

B:  func (n Node) height() int

我想知道您选择A或B中的哪一个以及为什么。

我可以为链表或递归结构建模,但我不知道接收器何时应该是指针还是非指针。

2 个答案:

答案 0 :(得分:5)

主要区别在于pass by value vs pass by reference

的想法

当您使用示例A时,您传递的内容是引用,这意味着您在n内应用于func (n *Node) height的所有更改都将适用于您所在的节点用来拨打Node.height()

相比之下,您在示例B中执行的操作是通过值传递,您实际上只是传递了一个用于调用Node.height()的节点的副本,因此任何更改该节点不会在该功能之外应用。

这是一个展示差异的小游乐场:http://play.golang.org/p/JodPRPBHDg

请注意,在示例中,当您致电node.incHeight()时,会转到功能:

func (n Node) incHeight() uint8 {
  n.value++
  return n.value
}

因为这是按值传递,node.value仍然相同,除非您将返回的值存储为node.value。但是,如果您致电node.incrementHeight(),则会转到功能:

func (n *Node) incrementHeight() {
  n.value++
  return
}

这将更改node.value的值,而不需要返回任何内容,因为它引用了原始结构,而不是结构的副本。

那么在回答你会选择哪个以及为什么,这取决于你是否希望能够对你用来调用该函数的结构进行持久的更改,或者你是否只是传递一个副本,该副本可以查看和更改值,但仅限于函数内,并且不会对原始结构产生任何持久影响。

答案 1 :(得分:4)

https://code.google.com/p/go-wiki/wiki/Style

中有一个非常好的解释
  

收件人类型

     

选择是否使用值或指针接收器   方法可能很难,特别是对于新的Go程序员。如果在   怀疑,使用指针,但有时候值接收器   感觉,通常是出于效率的原因,例如小的不变   结构或基本类型的值。一些经验法则:

     
      
  • 如果接收器是map,func或chan,请不要使用指向它的指针。

  •   
  • 如果接收器是一个切片,并且该方法没有重新切片或重新分配切片,请不要使用指向它的指针。

  •   
  • 如果方法需要改变接收器,接收器必须是指针。

  •   
  • 如果接收者是包含sync.Mutex或类似同步字段的结构,则接收者必须是避免复制的指针。

  •   
  • 如果接收器是一个大型结构或数组,则指针接收器更有效。大到多大?假设它相当于传球   其所有元素作为方法的参数。如果感觉太大,   它对于接收器来说也太大了。

  •   
  • 同时或从此方法调用时,函数或方法是否可以改变接收器?值类型创建副本   调用方法时的接收器,所以外部更新将   不适用于此接收器。如果必须在中显示更改   原始接收器,接收器必须是指针。

  •   
  • 如果接收器是结构,数组或切片,并且它的任何元素都是指向可能变异的东西的指针,则更喜欢指针
      接收器,因为它会使读者的意图更加清晰。

  •   
  • 如果接收器是一个小型数组或结构,它自然是一个值类型(例如,类似于time.Time类型),   没有可变字段和没有指针,或者只是一个简单的基本类型   比如int或string,值接收器是有意义的。一个值   接收器可以减少可以生成的垃圾量;如果   将值传递给value方法,可以使用堆栈上的副本   而不是在堆上分配。 (编译器试图变得聪明   关于避免这种分配,但它不能总是成功。)不要   为此原因选择值接收器类型而不进行分析   第一

  •   
  • 最后,如有疑问,请使用指针接收器。

  •