小能豆

golang指针调用方法的陷阱?

go

我正在写一个关于2-3-4树的实现。节点结构如下

type Node struct {
    items  []int
    childs []*Node
    parent *Node
}

我对下面的代码感到困惑。在我看来,这两部分正在做同样的事情。然而,其中之一是错误的。

  • 正确的代码
cur = cur.parent
cur._insertNode(upTo, rn)
upTo, rn = cur._splitNode()
  • 错误代码
cur.parent._insertNode(upTo, rn)
upTo, rn = cur.parent._splitNode()
cur = cur.parent

谁能告诉我有什么区别?

我期望的是对这个问题的解释。这是golang指针方法的陷阱吗?还是编译器错误?


阅读 69

收藏
2023-11-02

共1个答案

小能豆

在你的两段代码中,虽然它们在表面上看起来似乎在执行相同的操作,但实际上存在重要的区别。

首先,这涉及到Go语言中的指针和方法接收者的工作方式。让我们一步一步来看:

  1. 正确的代码块:
cur = cur.parent
cur._insertNode(upTo, rn)
upTo, rn = cur._splitNode()

在这个代码块中,首先将cur更新为其父节点(cur.parent),然后在新的cur上调用_insertNode方法,最后再对新的cur节点调用_splitNode方法。这是正确的方式,因为你正在将这些方法应用于新的cur节点。

  1. 错误的代码块:
cur.parent._insertNode(upTo, rn)
upTo, rn = cur.parent._splitNode()
cur = cur.parent

在这个错误的代码块中,你首先调用cur.parent._insertNode(upTo, rn),然后再调用cur.parent._splitNode(),最后才将cur设置为cur.parent。这样的问题在于,如果_insertNode方法或_splitNode方法中有修改cur.parent的逻辑,那么在后续调用cur.parent的方法时,你可能不再操作的是你以为的那个节点。

这个问题不是Go语言的指针或编译器错误,而是在Go中,方法接收者是通过值传递的,而不是引用传递。这意味着当你调用一个方法时,它会获得接收者的一个副本,而不是原始对象的引用。因此,在第一个正确的代码块中,你更新了cur,然后后续的方法调用作用在新的cur上。而在第二个错误的代码块中,你在同一对象上连续调用方法,如果这些方法中修改了cur.parent,你可能会意外地操作不同的对象。

因此,为了避免潜在的问题,你应该像在正确的代码块中那样,首先更新cur,然后再对新的cur节点执行操作。

2023-11-02