如何在Go中保留强有力的参考?

ale*_*ria 4 arrays pointers go slice

有什么方法可以在Go中保留一个强有力的参考?

给出以下复杂的代码:

package main

import (
  "fmt"
)

func main() {   
  slice := make([]int, 5)
  slice[3] = 25 // whatever index between 0 and 4 included I don't care

  slicesArray := make([]*[]int, 2)  
  slicesArray[0] = &slice

  fmt.Println((*(slicesArray[0]))[3])
  slice = nil
  fmt.Println((*(slicesArray[0]))[3])
}
Run Code Online (Sandbox Code Playgroud)

当然,这程序崩溃,因为一旦设定切片nil垃圾收集器标记的存储区域为脏.

但有没有办法告诉Go我的切片指针应该保留对这些切片的强引用?另外,在保持对切片的引用而不是将slicesArray声明为[][]int?时,是否有任何内存使用增益?是否有任何文件明确说明这应该如何工作?

icz*_*cza 6

TL; DR :(摘要)

您只需要复制切片值即可.slice类型的值是底层数组的描述符:

slice2 := slice
Run Code Online (Sandbox Code Playgroud)

现在slice2将引用相同的共享底层数组,因此在slicezerored 之后它将是可达的.

长期:

slice := make([]int, 5)
Run Code Online (Sandbox Code Playgroud)

这将创建一个名为slicetype 的局部变量[]int(并将使用一个描述符来初始化,该描述符引用5在后台创建的大小数组make).它是一个切片,它是在后台自动创建的基础数组的连续部分的描述符.

slicesArray[0] = &slice
Run Code Online (Sandbox Code Playgroud)

这将局部变量(命名slice)的地址存储到第0个元素中slicesArray.请注意,这slicesArray只是指针的一部分(指针可能指向类型的值,[]int但现在无关紧要).

因此,仍然没有slice创建原始副本.

因此,当您将类型的唯一值[]int(即已命名的局部变量slice)归零时,您将唯一的切片值归零(并且您将丢失对其支持数组的唯一引用).

您想slice在归零后保留切片值吗?只需制作它的副本(切片值).复制slice类型的值只会创建描述符的副本,不会复制后备数组; 并且副本将引用相同的后备数组(它是共享的):

slice2 := slice // makes a copy of the slice value
slice = nil
Run Code Online (Sandbox Code Playgroud)

在此之后*(slicesArray[0])仍将指向切片值nil,但我们有原始切片(和共享支持数组)的副本.

这样做:

slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])
Run Code Online (Sandbox Code Playgroud)

将再次打印25.去游乐场

你应该保留切片值或指向切片的指针吗?

由于切片只是相对较小的描述符,因此您应该保留并使用切片值.切片值已包含对后备阵列的"引用".通过使用指向切片的指针添加另一个间接只会使事情变得复杂并略微减慢速度.切片已经设计得小巧,高效和灵活(与Go中的"真实"数组相比).

当然,在某些情况下,指向切片值的指针也可能很有用,例如,如果您希望在append()不返回新切片的情况下创建类似于内置函数的函数.或者,当您创建其基础类型为切片类型的自定义类型时,您可以为此类型定义修改切片值的方法(在这种情况下,指针接收器是必需的).

进一步阅读:(详细解释所有内容的文档)

去切片:用法和内部

数组,切片(和字符串):'追加'的机制

  • @alediaferia数组可以包含元素.切片只是一个描述符,它指的是一个底层数组.在示例中,只分配了一个后备数组(通过这个:`make([] int,5)`).您可以根据需要使用多个切片引用相同的数组,只有切片描述符将在内存中"重复". (2认同)