欧美一区二区三区老妇人-欧美做爰猛烈大尺度电-99久久夜色精品国产亚洲a-亚洲福利视频一区二区

go語言中的淺拷貝 go語言復(fù)制文件

如何看待go語言泛型的最新設(shè)計?

Go 由于不支持泛型而臭名昭著,但最近,泛型已接近成為現(xiàn)實。Go 團隊實施了一個看起來比較穩(wěn)定的設(shè)計草案,并且正以源到源翻譯器原型的形式獲得關(guān)注。本文講述的是泛型的最新設(shè)計,以及如何自己嘗試泛型。

創(chuàng)新互聯(lián)是專業(yè)的蔡家坡網(wǎng)站建設(shè)公司,蔡家坡接單;提供網(wǎng)站設(shè)計、做網(wǎng)站,網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進(jìn)行蔡家坡網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!

例子

FIFO Stack

假設(shè)你要創(chuàng)建一個先進(jìn)先出堆棧。沒有泛型,你可能會這樣實現(xiàn):

type?Stack?[]interface{}func?(s?Stack)?Peek()?interface{}?{

return?s[len(s)-1]

}

func?(s?*Stack)?Pop()?{

*s?=?(*s)[:

len(*s)-1]

}

func?(s?*Stack)?Push(value?interface{})?{

*s?=?

append(*s,?value)

}

但是,這里存在一個問題:每當(dāng)你 Peek 項時,都必須使用類型斷言將其從 interface{} 轉(zhuǎn)換為你需要的類型。如果你的堆棧是 *MyObject 的堆棧,則意味著很多 s.Peek().(*MyObject)這樣的代碼。這不僅讓人眼花繚亂,而且還可能引發(fā)錯誤。比如忘記 * 怎么辦?或者如果您輸入錯誤的類型怎么辦?s.Push(MyObject{})` 可以順利編譯,而且你可能不會發(fā)現(xiàn)到自己的錯誤,直到它影響到你的整個服務(wù)為止。

通常,使用 interface{} 是相對危險的。使用更多受限制的類型總是更安全,因為可以在編譯時而不是運行時發(fā)現(xiàn)問題。

泛型通過允許類型具有類型參數(shù)來解決此問題:

type?Stack(type?T)?[]Tfunc?(s?Stack(T))?Peek()?T?{

return?s[len(s)-1]

}

func?(s?*Stack(T))?Pop()?{

*s?=?(*s)[:

len(*s)-1]

}

func?(s?*Stack(T))?Push(value?T)?{

*s?=?

append(*s,?value)

}

這會向 Stack 添加一個類型參數(shù),從而完全不需要 interface{}。現(xiàn)在,當(dāng)你使用 Peek() 時,返回的值已經(jīng)是原始類型,并且沒有機會返回錯誤的值類型。這種方式更安全,更容易使用。(譯注:就是看起來更丑陋,^-^)

此外,泛型代碼通常更易于編譯器優(yōu)化,從而獲得更好的性能(以二進(jìn)制大小為代價)。如果我們對上面的非泛型代碼和泛型代碼進(jìn)行基準(zhǔn)測試,我們可以看到區(qū)別:

type?MyObject?struct?{

X?

int

}

var?sink?MyObjectfunc?BenchmarkGo1(b?*testing.B)?{

for?i?:=?0;?i??b.N;?i++?{

var?s?Stack

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink?=?s.Peek().(MyObject)

}

}

func?BenchmarkGo2(b?*testing.B)?{

for?i?:=?0;?i??b.N;?i++?{

var?s?Stack(MyObject)

s.Push(MyObject{})

s.Push(MyObject{})

s.Pop()

sink?=?s.Peek()

}

}

結(jié)果:

BenchmarkGo1BenchmarkGo1-16?????12837528?????????87.0?ns/op???????48?B/op????????2?allocs/opBenchmarkGo2BenchmarkGo2-16?????28406479?????????41.9?ns/op???????24?B/op????????2?allocs/op

在這種情況下,我們分配更少的內(nèi)存,同時泛型的速度是非泛型的兩倍。

合約(Contracts)

上面的堆棧示例適用于任何類型。但是,在許多情況下,你需要編寫僅適用于具有某些特征的類型的代碼。例如,你可能希望堆棧要求類型實現(xiàn) String() 函數(shù)

深拷貝和淺拷貝怎樣理解(通俗具體點兒)

1.深拷貝與淺拷貝 拷貝即是通常所說的復(fù)制(Copy)或克隆(Clone),對象的拷貝也就是從現(xiàn)有對象復(fù)制一個“一模一樣”的新對象出來。雖然都是復(fù)制對象,但是不同的復(fù)制方法,復(fù)制出來的新對象卻并非完全一模一樣,對象內(nèi)部存在著一些差異。通常的拷貝方法有兩種,即深拷貝和淺拷貝,那二者之間有何區(qū)別呢?MSDN里對IClone接口的Clone方法有這樣的說明:在深層副本中,所有的對象都是重復(fù)的;而在淺表副本中,只有頂級對象是重復(fù)的,并且頂級以下的對象包含引用。可以看出,深拷貝和淺拷貝之間的區(qū)別在于是否復(fù)制了子對象。這如何理解呢?下面我通過帶有子對象的代碼來驗證二者的區(qū)別。 首先定義兩個類型:Student和ClassRoom,其中Student類型里包含ClassRoom,并使這兩個類型都分別實現(xiàn)自定義的深拷貝接口(IDeepCopy)和淺拷貝接口(IShallowCopy)。 類圖如下: 定義代碼如下: 定義代碼 /// summary /// 深拷貝接口 /// /summary interface IDeepCopy { object DeepCopy(); } /// summary /// 淺拷貝接口 /// /summary interface IShallowCopy { object ShallowCopy(); } /// summary /// 教室信息 /// /summary class ClassRoom : IDeepCopy, IShallowCopy { public int RoomID = 1; public string RoomName = "Room1"; public override string ToString() { return "RoomID=" + RoomID + "\tRoomName=" + RoomName; } public object DeepCopy() { ClassRoom r = new ClassRoom(); r.RoomID = this.RoomID; r.RoomName = this.RoomName; return r; } public object ShallowCopy() { //直接使用內(nèi)置的淺拷貝方法返回 return this.MemberwiseClone(); } } class Student : IDeepCopy, IShallowCopy { //為了簡化,使用public 字段 public string Name; public int Age; //自定義類型,假設(shè)每個Student只擁有一個ClassRoom public ClassRoom Room = new ClassRoom(); public Student() { } public Student(string name, int age) { this.Name = name; this.Age = age; } public object DeepCopy() { Student s = new Student(); s.Name = this.Name; s.Age = this.Age; s.Room = (ClassRoom)this.Room.DeepCopy(); return s; } public object ShallowCopy() { return this.MemberwiseClone(); } public override string ToString() { return "Name:" + Name + "\tAge:" + Age + "\t" + Room.ToString(); } } 測試代碼: 測試代碼 Student s1 = new Student("Vivi", 28); Console.WriteLine("s1=[" + s1 + "]"); Student s2 = (Student)s1.ShallowCopy(); //Student s2 = (Student)s1.DeepCopy(); Console.WriteLine("s2=[" + s2 + "]"); //此處s2和s1內(nèi)容相同 Console.WriteLine("-----------------------------"); //修改s2的內(nèi)容 s2.Name = "tianyue"; s2.Age = 25; s2.Room.RoomID = 2; s2.Room.RoomName = "Room2"; Console.WriteLine("s1=[" + s1 + "]"); Console.WriteLine("s2=[" + s2 + "]"); //再次打印兩個對象以比較 Console.ReadLine(); 運行結(jié)果: a.ShallowCopy s1=[Name:Vivi Age:28 RoomID=1 RoomName=Room1] s2=[Name:Vivi Age:28 RoomID=1 RoomName=Room1] ------------------------------------------------------------- s1=[Name:Vivi Age:28 RoomID=2 RoomName=Room2] s2=[Name:tianyue Age:25 RoomID=2 RoomName=Room2] b.DeepCopy s1=[Name:Vivi Age:28 RoomID=1 RoomName=Room1] s2=[Name:Vivi Age:28 RoomID=1 RoomName=Room1] ----------------------------- s1=[Name:Vivi Age:28 RoomID=1 RoomName=Room1] s2=[Name:tianyue Age:25 RoomID=2 RoomName=Room2] 從以上結(jié)果可以看出,深拷貝時兩個對象是完全“分離”的,改變其中一個,不會影響到另一個對象;淺拷貝時兩個對象并未完全“分離”,改變頂級對象的內(nèi)容,不會對另一個對象產(chǎn)生影響,但改變子對象的內(nèi)容,則兩個對象同時被改變。這種差異的產(chǎn)生,即是取決于拷貝子對象時復(fù)制內(nèi)存還是復(fù)制指針。深拷貝為子對象重新分配了一段內(nèi)存空間,并復(fù)制其中的內(nèi)容;淺拷貝僅僅將指針指向原來的子對象。 示意圖如下: 2.淺拷貝與賦值操作 大多數(shù)面向?qū)ο笳Z言中的賦值操作都是傳遞引用,即改變對象的指針地址,而并沒有復(fù)制內(nèi)存,也沒有做任何復(fù)制操作。由此可知,淺拷貝與賦值操作的區(qū)別是頂級對象的復(fù)制與否。當(dāng)然,也有一些例外情況,比如類型定義中重載賦值操作符(assignment operator),或者某些類型約定按值傳遞,就像C#中的結(jié)構(gòu)體和枚舉類型。 賦值操作示意圖如下: 3.C++拷貝構(gòu)造函數(shù) 與其它面向?qū)ο笳Z言不同,C++允許用戶選擇自定義對象的傳遞方式:值傳遞和引用傳遞。在值傳遞時就要使用對象拷貝,比如說按值傳遞參數(shù),編譯器需要拷貝一個對象以避免原對象在函數(shù)體內(nèi)被破壞。為此,C++提供了拷貝構(gòu)造函數(shù)用來實現(xiàn)這種拷貝行為,拷貝構(gòu)造函數(shù)是一種特殊的構(gòu)造函數(shù),用來完成一些基于同一類的其它對象的構(gòu)造和初始化。它唯一的參數(shù)是引用類型的,而且不可改變,通常的定義為X(const X)。在拷貝構(gòu)造函數(shù)里,用戶可以定義對象的拷貝行為是深拷貝還是淺拷貝,如果用戶沒有實現(xiàn)自己的拷貝構(gòu)造函數(shù),那么編譯器會提供一個默認(rèn)實現(xiàn),該實現(xiàn)使用的是按位拷貝(bitwise copy),也即本文所說的淺拷貝。構(gòu)造函數(shù)何時被調(diào)用呢?通常以下三種情況需要拷貝對象,此時拷貝構(gòu)造函數(shù)將會被調(diào)用。 1.一個對象以值傳遞的方式傳入函數(shù)體 2.一個對象以值傳遞的方式從函數(shù)返回 3.一個對象需要通過另外一個對象進(jìn)行初始化 4.C# MemberwiseClone與ICloneable接口 和C++里的拷貝構(gòu)造函數(shù)一樣,C#也為每個對象提供了淺拷貝的默認(rèn)實現(xiàn),不過C#里沒有拷貝構(gòu)造函數(shù),而是通過頂級類型Object里的MemberwiseClone方法。MemberwiseClone 方法創(chuàng)建一個淺表副本,方法是創(chuàng)建一個新對象,然后將當(dāng)前對象的非靜態(tài)字段復(fù)制到該新對象。有沒有默認(rèn)的深拷貝實現(xiàn)呢?當(dāng)然是沒有,因為需要所有參與拷貝的對象定義自己的深拷貝行為。C++里需要用戶實現(xiàn)拷貝構(gòu)造函數(shù),重寫默認(rèn)的淺拷貝;C#則不同,C#(確切的說是.NET Framework,而非C#語言)提供了ICloneable 接口,包含一個成員 Clone,它用于支持除 MemberwiseClone 所提供的克隆之外的克隆。C++通過拷貝構(gòu)造函數(shù)無法確定子對象實現(xiàn)的是深拷貝還是淺拷貝,而C#在“強制”實現(xiàn)淺拷貝的基礎(chǔ)上,提供ICloneable 接口由用戶定義深拷貝行為,通過接口來強制約束所有參與拷貝的對象,個人覺得,這也算是一小點C#對C++的改進(jìn)。 5.深拷貝策略與實現(xiàn) 深拷貝的要點就是確保所有參與拷貝的對象都要提供自己的深拷貝實現(xiàn),不管是C++拷貝構(gòu)造函數(shù)還是C#的ICloneable 接口,事實上都是一種拷貝的約定。有了事先的約定,才能約束實現(xiàn)上的統(tǒng)一,所以關(guān)鍵在于設(shè)計。 但偶爾也會在后期才想到要深拷貝,怎么辦?總不能修改所有之前的實現(xiàn)吧。有沒有辦法能夠通過頂級類而不關(guān)心內(nèi)部的子對象直接進(jìn)行深拷貝呢?能不能搞個萬能的深拷貝方法,在想用的時候立即用,而不考慮前期的設(shè)計。這樣“大包大攬”的方法,難點在于實現(xiàn)時必須自動獲取子對象的信息,分別為子對象實現(xiàn)深拷貝。C++里比較困難,.NET的反射機制使得實現(xiàn)容易一些。不過這樣的方法雖然通用,實則破壞了封裝,也不符合“每個類對自己負(fù)責(zé)”的設(shè)計原則。 基于.NET的反射機制,以前寫了一個通用的序列化方法,現(xiàn)在可以拿過來,先序列化,然后再反序列化回來,也即是一個深拷貝,示例代碼如下: 深拷貝示例代碼 #region ICloneable Members /// summary /// 此處的復(fù)制為深拷貝,在實現(xiàn)上,為了簡化,采用序列化和反序列化。 /// /summary /// returns深拷貝對象/returns public object Clone() { Student stu = new Student(); XmlStorageHelper helper = new XmlStorageHelper(); string strXml = helper.ConvertToString(this); helper.LoadFromString(stu, strXml); //從XML字符串來賦值 return stu; } #endregion

深拷貝和淺拷貝是c語言中還是c++中的

淺拷貝還是深拷貝這是對于指針來說的,基本變量是沒有這一說的,都是直接分配一個新的內(nèi)存給它。

所string類是stl類,已經(jīng)封裝好了,當(dāng)然是深度拷貝,直接用就可以,完全不用擔(dān)心內(nèi)存的問題,經(jīng)過幾十年的安全檢驗了

Go切片數(shù)組深度解析

Go 中的分片數(shù)組,實際上有點類似于Java中的ArrayList,是一個可以擴展的數(shù)組,但是Go中的切片由比較靈活,它和數(shù)組很像,也是基于數(shù)組,所以在了解Go切片前我們先了解下數(shù)組。

數(shù)組簡單描述就由相同類型元素組成的數(shù)據(jù)結(jié)構(gòu), 在創(chuàng)建初期就確定了長度,是不可變的。

但是Go的數(shù)組類型又和C與Java的數(shù)組類型不一樣, NewArray 用于創(chuàng)建一個數(shù)組,從源碼中可以看出最后返回的是 Array{}的指針,并不是第一個元素的指針,在Go中數(shù)組屬于值類型,在進(jìn)行傳遞時,采取的是值傳遞,通過拷貝整個數(shù)組。Go語言的數(shù)組是一種有序的struct。

Go 語言的數(shù)組有兩種不同的創(chuàng)建方式,一種是顯示的初始化,一種是隱式的初始化。

注意一定是使用 [...]T 進(jìn)行創(chuàng)建,使用三個點的隱式創(chuàng)建,編譯器會對數(shù)組的大小進(jìn)行推導(dǎo),只是Go提供的一種語法糖。

其次,Go中數(shù)組的類型,是由數(shù)值類型和長度兩個一起確定的。[2]int 和 [3]int 不是同一個類型,不能進(jìn)行傳參和比較,把數(shù)組理解為類型和長度兩個屬性的結(jié)構(gòu)體,其實就一目了然了。

Go中的數(shù)組屬于值類型,通常應(yīng)該存儲于棧中,局部變量依然會根據(jù)逃逸分析確定存儲棧還是堆中。

編譯器對數(shù)組函數(shù)中做兩種不同的優(yōu)化:

在靜態(tài)區(qū)完成賦值后復(fù)制到棧中。

總結(jié)起來,在不考慮逃逸分析的情況下,如果數(shù)組中元素的個數(shù)小于或者等于 4 個,那么所有的變量會直接在棧上初始化,如果數(shù)組元素大于 4 個,變量就會在靜態(tài)存儲區(qū)初始化然后拷貝到棧上。

由于數(shù)組是值類型,那么賦值和函數(shù)傳參操作都會復(fù)制整個數(shù)組數(shù)據(jù)。

不管是賦值或函數(shù)傳參,地址都不一致,發(fā)生了拷貝。如果數(shù)組的數(shù)據(jù)較大,則會消耗掉大量內(nèi)存。那么為了減少拷貝我們可以主動的傳遞指針呀。

地址是一樣的,不過傳指針會有一個弊端,從打印結(jié)果可以看到,指針地址都是同一個,萬一原數(shù)組的指針指向更改了,那么函數(shù)里面的指針指向都會跟著更改。

同樣的我們將數(shù)組轉(zhuǎn)換為切片,通過傳遞切片,地址是不一樣的,數(shù)組值相同。

切片是引用傳遞,所以它們不需要使用額外的內(nèi)存并且比使用數(shù)組更有效率。

所以,切片屬于引用類型。

通過這種方式可以將數(shù)組轉(zhuǎn)換為切片。

中間不加三個點就是切片,使用這種方式創(chuàng)建切片,實際上是先創(chuàng)建數(shù)組,然后再通過第一種方式創(chuàng)建。

使用make創(chuàng)建切片,就不光編譯期了,make創(chuàng)建切片會涉及到運行期。1. 切片的大小和容量是否足夠小;

切片是否發(fā)生了逃逸,最終在堆上初始化。如果切片小的話會先在棧或靜態(tài)區(qū)進(jìn)行創(chuàng)建。

切片有一個數(shù)組的指針,len是指切片的長度, cap指的是切片的容量。

cap是在初始化切片是生成的容量。

發(fā)現(xiàn)切片的結(jié)構(gòu)體是數(shù)組的地址指針array unsafe.Pointer,而Go中數(shù)組的地址代表數(shù)組結(jié)構(gòu)體的地址。

slice 中得到一塊內(nèi)存地址,array[0]或者unsafe.Pointer(array[0])。

也可以通過地址構(gòu)造切片

nil切片:指的unsafe.Pointer 為nil

空切片:

創(chuàng)建的指針不為空,len和cap為空

當(dāng)一個切片的容量滿了,就需要擴容了。怎么擴,策略是什么?

如果原來數(shù)組切片的容量已經(jīng)達(dá)到了最大值,再想擴容, Go 默認(rèn)會先開一片內(nèi)存區(qū)域,把原來的值拷貝過來,然后再執(zhí)行 append() 操作。這種情況對現(xiàn)數(shù)組的地址和原數(shù)組地址不相同。

從上面結(jié)果我們可以看到,如果用 range 的方式去遍歷一個切片,拿到的 Value 其實是切片里面的值拷貝,即淺拷貝。所以每次打印 Value 的地址都不變。

由于 Value 是值拷貝的,并非引用傳遞,所以直接改 Value 是達(dá)不到更改原切片值的目的的,需要通過 slice[index] 獲取真實的地址。

Golang復(fù)制結(jié)構(gòu)體

Golang中復(fù)制結(jié)構(gòu)體,可以使用賦值語句

執(zhí)行結(jié)果

可以看出,roger跟mydog在內(nèi)存中的地址不同。并且對mydog修改屬性,對roger沒有影響。

但是注意,這里的Dog結(jié)構(gòu)體中的屬性,都是值類型。如果是 引用類型 的話,復(fù)制的是 指針 ,而不是具體的值。所以通過賦值語句對結(jié)構(gòu)體的拷貝,是 淺拷貝 。如需對引用類型屬性進(jìn)行深拷貝,可以通過手動創(chuàng)建的方式,或者使用實現(xiàn)了deepcopy功能的第三方包

什么是淺拷貝(shallow copy)和深拷貝(deep copy)

A:淺拷貝就是成員數(shù)據(jù)之間的一一賦值:把值賦給一一賦給要拷貝的值。但是可能會有這樣的情況:對象還包含資源,這里的資源可以值堆資源,或者一個文件。。當(dāng)值拷貝的時候,兩個對象就有用共同的資源,同時對資源可以訪問,這樣就會出問題。深拷貝就是用來解決這樣的問題的,它把資源也賦值一次,使對象擁有不同的資源,但資源的內(nèi)容是一樣的。對于堆資源來說,就是在開辟一片堆內(nèi)存,把原來的內(nèi)容拷貝。

如果你拷貝的對象中引用了某個外部的內(nèi)容(比如分配在堆上的數(shù)據(jù)),那么在拷貝這個對象的時候,讓新舊兩個對象指向同一個外部的內(nèi)容,就是淺拷貝;如果在拷貝這個對象的時候為新對象制作了外部對象的獨立拷貝,就是深拷貝

引用和指針的語義是相似的,引用是不可改變的指針,指針是可以改變的引用。其實都是實現(xiàn)了引用語義。

深拷貝和淺拷貝的區(qū)別是在對象狀態(tài)中包含其它對象的引用的時候,當(dāng)拷貝一個對象時,如果需要拷貝這個對象引用的對象,則是深拷貝,否則是淺拷貝。

COW語義是“深拷貝”與“推遲計算”的組合,仍然是深拷貝,而非淺拷貝,因為拷貝之后的兩個對象的數(shù)據(jù)在邏輯上是不相關(guān)的,只是內(nèi)容相同。

舉個簡單的例子:

當(dāng)你實現(xiàn)一個Composite Pattern,你通常都會實現(xiàn)一個深拷貝(如果需要拷貝的話),很少有要求同的Composite共享Leaf的;

而當(dāng)你實現(xiàn)一個Observer Pattern時,如果你需要拷貝Observer,你大概不會去拷貝Subject,這時就要實現(xiàn)個淺拷貝。

是深拷貝還是淺拷貝,并不是取決于時間效率、空間效率或是語言等等,而是取決于哪一個是邏輯上正確的。

1:沒有虛方法和虛基類

2:所有直系基類的copy constructor都是無代價的

3:所有成員的copy constructor都是無代價的

這時它的copy constructor是無代價的,相當(dāng)于用memcpy實現(xiàn)。

判斷它是深拷貝還是淺拷貝,還是要根據(jù)類的實現(xiàn)。

本文標(biāo)題:go語言中的淺拷貝 go語言復(fù)制文件
鏈接地址:http://chinadenli.net/article18/hijjdp.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供電子商務(wù)定制網(wǎng)站移動網(wǎng)站建設(shè)域名注冊企業(yè)建站全網(wǎng)營銷推廣

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站網(wǎng)頁設(shè)計