2015-06-16 23 views
4

有沒有什麼辦法可以在Go中保持強有力的參考?如何在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]) 
} 

當然崩潰的該程序,因爲切片一旦設定爲nil垃圾收集標記的存儲區爲髒。

但是有沒有辦法告訴Go,我對片的指針切片應該保持對這些片的強引用? 另外,在保持對切片的引用時是否有任何內存使用增益,而不是將切片陣列聲明爲[][]int?有沒有任何文件明確說明這應該如何工作?

+1

「因爲切片一旦設定爲nil垃圾收集標記的存儲區域爲髒」,這是不正確。如果任何代碼有一個指向某個東西的指針,那麼垃圾收集器就把它留下。你可以整天設置指針爲'nil',垃圾收集器沒有任何反應(**除非**指向的內容現在不再被其他任何東西指向)。 –

回答

6

TL; DR:(摘要)

你只需要做切片值的副本。片類型的值是一個描述符到一個底層陣列:

slice2 := slice 

現在slice2將是指相同的,共享基礎數組等slice被zerored後它將是可到達的。

在長:

slice := make([]int, 5) 

這創建名爲[]int類型的slice局部變量(和將被與描述符指由make在後臺創建大小5的數組初始化)。它是一個分片,它是底層數組的一個連續部分的描述符,它是在後臺自動創建的。

slicesArray[0] = &slice 

此存儲局部變量(名爲slice)插入的slicesArray第0個元素的地址。請注意,slicesArray只是一個指針片(指針可指向[]int類型的值,但現在無關緊要)。

因此,仍然沒有創建原始slice的副本。

因此,當您將類型爲[]int(這是本地變量名爲slice)的唯一值歸零時,您只會歸零唯一的切片值(並且您將失去對其後備數組的唯一引用)。

你想在slice被清零後保持切片值?只需製作一份(切片值)的副本。複製切片類型的值只會生成描述符的副本,不會複製後備數組;而副本將指向同一個支持數組(它的共享):

slice2 := slice // makes a copy of the slice value 
slice = nil 

*(slicesArray[0])仍將指向一個切片值爲nil後,但我們有原片的副本(和共享支持數組)。

這樣算下來:

slicesArray[0] = &slice2 
fmt.Println((*(slicesArray[0]))[3]) 

將打印再次25Go Playground

您應該保留切片值還是指向切片?

由於切片只是相對較小的描述符,因此您應該保留並使用切片值。切片值已包含對後備陣列的「引用」。通過使用指向切片的指針添加另一個間接方向會使事情複雜化並稍微放慢速度。切片已經被設計成小巧,高效和靈活(與Go中的「真實」陣列相比)。

當然,在某些情況下,指向切片值的指針也可能很有用,例如,如果您希望創建一個類似於內建函數的函數而不返回新切片。或者,當您創建基礎類型爲切片類型的自定義類型時,並且您可以爲此類型定義修改切片值的方法(在這種情況下,指針接收器是必需的)。

此外讀數:(該文檔在詳細解釋一切)

Go Slices: usage and internals

Arrays, slices (and strings): The mechanics of 'append'

+0

那麼使用COW方法的內建'copy'函數(或':='操作符,不管)?我的意思是,對於保存值爲3的整數的片只分配一次內存嗎? – alediaferia

+0

或者,更好的是:這個例子使用了和你一樣的內存量嗎? http://play.golang.org/p/e_ELjfseCO – alediaferia

+2

@alediaferia一個數組可以容納元素。切片只是一個描述符,它指的是一個底層數組。在這個例子中,只有一個支持數組被分配(通過:'make([] int,5)')。您可以根據需要將盡可能多的片段引用到同一個陣列,只有片段描述符將在內存中「複製」。 – icza