【问题标题】:Go: reference types as argumentsGo:引用类型作为参数
【发布时间】:2015-07-02 22:14:30
【问题描述】:

Go 中的某些类型是引用类型:映射、切片、通道、函数和方法。

有时您需要使用指向引用的指针。例如,

type Stack []interface{}
func (stack *Stack) Push(x interface{}) { 
    *stack = append(*stack, x)
}

您需要它,因为所有参数都是通过复制值来传递的,而append() 可能需要在切片容量不够大的情况下重新分配内存。我明白了。

第一个问题。map 类型怎么样?如果我有一个基于 map 的自定义类型,如果需要一些键值插入或删除,我是否应该始终传递一个指向它的指针?

第二个问题。其他引用类型呢?例如Channel。我可以想象这样一种情况,我基于通道构建自定义类型,以对传递给通道的值实现一些自定义预处理。这里也需要指针吗?

对不起,如果这是基本的,但我真的想很好地掌握这个主题。

【问题讨论】:

  • 您将切片描述符与底层数组混淆了。 Go 中没有引用类型。你有价值观,你有指针。指针是“参考”。而且我不知道您的代码示例在做什么,但这当然不是必需的。值得一读blog.golang.org/go-slices-usage-and-internals
  • @evanmcdonnal:不,这需要一个指针,因为附加可能会分配一个新数组,从而更改内部数据指针以及 len 和 cap 值。 play.golang.org/p/Cm8AQXpK5J
  • @JimB 是的,我在操场上修补它时注意到了这一点。这种必要性是由愚蠢的设计引起的。只需使用接收器和返回类型Stack 定义方法并执行myStack = myStack.Push(x)。或者更好的是,不要定义这些,因为这只是浪费您的系统 RAM。就像它这样做的唯一原因是因为 append 将切片描述符作为一个值处理,在操作完成后返回一个新的。然后他做了一些不一致的事情来使他的类型只不过是一个切片链使用指针的方法。
  • @evanmcdonnal:使用这样的自定义类型是完全有理由的。例如,查看 stdlib 中的 container/heapsort 包(它显示了您不需要需要指针的位置)。在整个 stdlib 中还有更多这样的自定义类型的示例,包括 map 和 func 类型。
  • Go 中没有引用类型。例如。 slice 不是引用类型,而 chanel 也不是引用类型。 Go 中最接近引用类型的类型是映射。停止使用“引用类型”真的很有帮助:没有。

标签: parameters go reference


【解决方案1】:

当您将所有内容都视为一个值时,规则相当简单,其中一些值在内部包含指针。

  • 切片:当您可能需要修改长度或容量时使用指针,这会改变切片的值。
  • 地图:不要使用指针,因为地图值不会随修改而改变。
  • 函数和方法:不要使用指针,通过函数值具有相同的效果。
  • chan:不要使用指针。

当然也有例外,例如,如果您希望能够完全换出地图,则需要使用指针来执行此操作,但这种情况很少见。

【讨论】:

  • 您不必同步对指向通道的指针的修改。如果修改仅由单个 goroutine 发生,则不需要同步(就像所有指针一样)。
  • @rightfold: 好吧,如果只有一个 goroutine 中的访问,则不需要同步任何内容,但即使该值仅在单个 goroutine 中修改,不同步的并发读取该值仍然是竞争条件。但是,该条件与同一范围内的共享 chan 值相同。我将删除措辞,因为它可能比有用更令人困惑。
【解决方案2】:

“值类型”和“引用类型”之间并没有真正的二分法。 “引用类型”只是用来描述一个“值”完全由单个指针组成的值类型。

map 和 channel 类型也是如此,它们基本上是指向内部结构的指针类型。但这对于切片来说并不完全正确,因为切片是一种复合类型(基本上是一个结构),由两个整数值(长度和容量)和一个指针(指向元素)组成。所以它对于通过指针访问的元素来说是一个“引用类型”,但对于长度和容量来说它是一个“值类型”。

附加到切片对其长度和潜在容量进行操作,因此它需要更改切片的“值”,而就地分配元素只使用指针,因此不需要更改“值”的切片。如果您希望切片的指针更改为指向与另一个切片相同的指针(您可以通过分配给切片来实现),您可能还需要更改切片的“值”。

“参考类型”、地图和通道类似。更改地图或通道的“内容”(在指针指向的内容中)不需要更改地图或通道的“值”。但是,如果您想更改指针以指向不同的底层地图或通道,那么您将更改地图或通道变量的“值”。

【讨论】:

  • 虽然这是一个很好的答案,但它不应该以““值类型”和“引用类型”之间没有真正的二分法”开头。值类型(例如结构体在作为函数参数传递时涉及复制内容)与类型(例如映射)不涉及复制作为函数参数传递的映射。 “差异”是否足够弱于“二分法”,允许在明显“存在差异”的情况下声明“不存在二分法”,这是值得商榷和混淆的。
猜你喜欢
  • 2014-06-23
  • 1970-01-01
  • 2015-04-24
  • 2018-12-22
  • 2016-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-08-23
相关资源
最近更新 更多