這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
續日今天問我:Golang裡面如果做結構體的深拷貝呢?他說他的excelize庫被網友爆出一個Bug:
改變sheet2頁上的資料,sheet1上的資料會一起發生變化,最後他定位到問題是由於自己做了兩個sheet結構體的淺拷貝導致的。因為sheet對應的結構體的特殊性其必須是指標類型。是啊。。。這樣就坑了,Golang裡面怎麼做一個超級無敵複雜結構體的指標變數的深拷貝呢?給個例子:
type KDeepCopy struct { A map[string]string B []string C struct { D [][]string E map[interface{}]F G string K func() }}type F struct { K map[int]*string}
var k *KDeepCopy = &KDeepCopy{....} , var h *KDeepCopy。 這個時候想把k這個變數的記憶體中的值全部深拷貝到h這個變數上,即k和h指向不同記憶體地區。C++裡面我們可以使用memcpy函數(或者類似的函數)來完成。但Golang官方沒有提供這樣的API函數給我們直接操作記憶體。這裡給出一個解決方案(續神提供的),我寫了簡單的demo代碼驗證。demo中給了json和gob兩種方法去解決,這兩種編解碼方案哪種更好,自己評鑒。
package mainimport ( "bytes" "encoding/gob" "encoding/json" "fmt")type GDeepCopy struct { A map[string]string B []string D int}type DeepCopy struct { A map[string]string B []string C Cc}type Cc struct { D map[int]*string}func main() { var cc Cc = Cc{ D: make(map[int]*string, 0), } var dc DeepCopy = DeepCopy{ A: make(map[string]string, 0), B: []string{`b`, `c`, `d`}, C: cc, } dv := DeepCopy{} dv = dc fmt.Printf("dc : %p\n", &dc) fmt.Printf("dv: %p\n", &dv) dv.B = []string{`f`, `g`, `d`} fmt.Println(`dc:`, dc) fmt.Println(`dv:`, dv) fmt.Println(`=============================`) var df *DeepCopy = &DeepCopy{ A: make(map[string]string, 0), B: []string{`b`, `c`, `d`}, C: cc, } dn := &DeepCopy{} dn = df fmt.Printf("dn : %p\n", dn) fmt.Printf("df: %p\n", df) dn.B = []string{`f`, `g`, `d`} fmt.Println(`df:`, df) fmt.Println(`dn:`, dn) fmt.Println(`=============================`) var buf bytes.Buffer dg := &DeepCopy{} json.NewEncoder(&buf).Encode(*df) json.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dg) dg.B = []string{`h`, `j`, `k`} fmt.Printf("df : %p\n", df) fmt.Printf("dg: %p\n", dg) fmt.Println(`df:`, df) fmt.Println(`dg:`, dg) fmt.Println(`=============================`) dj := &GDeepCopy{} gob.NewEncoder(&buf).Encode(*df) gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dj) dj.B = []string{`q`, `w`, `e`} fmt.Printf("df : %p\n", df) fmt.Printf("dj: %p\n", dj) fmt.Println(`df:`, df) fmt.Println(`dj:`, dj) fmt.Println(`=============================`)}
是的,可以通過先把struct序列化儲存到一個buffer裡面,然後將buffer裡面的Byte()資料進行還原序列化到執行對象裡面即可。是不是感覺好麻煩呢?如果你有什麼好的方案,請在評論區貼出。