C++直接初始化和复制初始化
(1)復(fù)制初始化的基本原理
我們知道,對(duì)象在內(nèi)存中的直接表象是在內(nèi)存中占有一個(gè)一定大小的空間。分配空間是建立對(duì)象的第一步。但是剛剛分配的空間就像一個(gè)沒有開墾的荒田,或者是前面對(duì)象占有之后留下來(lái)的殘余,從理論上講,由于不管是什么大家都是0101,所以就算沒有初始化也是有值的,只是這個(gè)值你是不能正確使用的。所以建立對(duì)象的關(guān)鍵在于如何對(duì)該對(duì)象所占有的空間進(jìn)行正確的初始化。
初始化雖然與賦值的結(jié)果很像,但是其所面臨的狀態(tài)和發(fā)生的時(shí)間是不同的。從他們的功能來(lái)講,初始化與給某個(gè)變量賦值的唯一特點(diǎn)就是他是第一次初始化。當(dāng)然當(dāng)你認(rèn)識(shí)到這點(diǎn)的時(shí)候,他們的區(qū)別也就是無(wú)所謂了。關(guān)鍵在于,初始化,是一個(gè)必須的賦值過(guò)程,因?yàn)槿绻悴蛔鲞@方面的動(dòng)作,你的對(duì)像是一個(gè)沒有用的(或者不能正常使用的)
直接初始化,就是使用構(gòu)造函數(shù),定義在申請(qǐng)了對(duì)象空間之后如何對(duì)各個(gè)子空間進(jìn)行初始賦值,稱他們?yōu)橹苯映跏蓟呛苜N切的,應(yīng)為他就是最為普通的初始化,是構(gòu)建一個(gè)完整對(duì)象的一個(gè)過(guò)程——先將空間申請(qǐng)好,然后給各個(gè)子空間(數(shù)據(jù)屬性)進(jìn)行相應(yīng)的賦值。
復(fù)制初始化,特點(diǎn)特別之處在于“復(fù)制”二字,核心意義就是,我通過(guò)對(duì)一個(gè)已有對(duì)象的完全復(fù)制,來(lái)構(gòu)建對(duì)象。它的過(guò)程可以理解成這樣——先申請(qǐng)空間,然后將被復(fù)制的對(duì)象(空間一樣大)的所有內(nèi)容全部復(fù)制過(guò)去,就形成了這個(gè)對(duì)象。所以,要明確,兩種初始化的方式,都是一構(gòu)造函數(shù)的形式存在的。區(qū)別在于他們的參數(shù)方面,復(fù)制初始化,表達(dá)的就是對(duì)一個(gè)已有同類型的對(duì)象進(jìn)行復(fù)制,那么這種構(gòu)造函數(shù)就應(yīng)該使用某個(gè)對(duì)象來(lái)進(jìn)行復(fù)制,同時(shí)因?yàn)?#xff0c;C++參數(shù)的傳遞默認(rèn)都是值傳遞,要聲明使用引用的方式(要不然就又復(fù)制了一次了);并且是const類型的。
也就是說(shuō)任何復(fù)制初始化的表面特征就是使用“=”號(hào)來(lái)表達(dá),左邊是對(duì)該對(duì)象的空間的申明,右邊是另外一個(gè)同類型的對(duì)象,注意,一定是同類型的對(duì)象(即使不是,也會(huì)使用類型轉(zhuǎn)換構(gòu)造函數(shù)來(lái)進(jìn)行構(gòu)造(前面說(shuō)過(guò))!形如:
這里的操作有兩個(gè)過(guò)程,先使用對(duì)象構(gòu)造函數(shù)通過(guò)直接初始化構(gòu)造出一個(gè)對(duì)象,然后將指針?lè)呕?#xff0c;不管有沒有參數(shù)。
這里表達(dá)的是,這個(gè)othe_objectname3,不是classname這個(gè)類型。這個(gè)個(gè)就要知道,右邊的會(huì)通過(guò)調(diào)用直接初始化構(gòu)造函數(shù),構(gòu)造出對(duì)應(yīng)的對(duì)象,然后調(diào)用復(fù)制初始化構(gòu)造函數(shù)。
所以:?構(gòu)造函數(shù)應(yīng)該分為:1)直接初始化構(gòu)造函數(shù)
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2)復(fù)制初始化構(gòu)造函數(shù)
還是要說(shuō),復(fù)制初始化構(gòu)造函數(shù)看起來(lái)像賦值,但是,其實(shí)只是像而已,它其實(shí)是借用了這種表象,來(lái)觸發(fā)調(diào)用了那個(gè)使用同類型對(duì)象引用作為參數(shù)的復(fù)制構(gòu)造函數(shù)。
(2)構(gòu)造函數(shù)(用于直接或復(fù)制初始化?)的使用模式。
事實(shí)上,我們說(shuō)這些東西特殊,關(guān)鍵是他們時(shí)候的時(shí)候特殊,兩種構(gòu)造函數(shù)(對(duì)應(yīng)兩種初始化方式)都有顯示和隱式的構(gòu)造方法。
對(duì)于直接初始化,我們知道顯示的方法,就是使用類名加上參數(shù)(其實(shí)就是在調(diào)用構(gòu)造函數(shù)),隱式的方法,就是在一個(gè)需要該類對(duì)象的地方出現(xiàn)了其他的數(shù)據(jù)類型,于是系統(tǒng)會(huì)自動(dòng)調(diào)用對(duì)應(yīng)函數(shù)(這個(gè)應(yīng)該在編譯的時(shí)候應(yīng)該調(diào)用了)因?yàn)檫@個(gè)時(shí)候可以檢測(cè)出錯(cuò)誤來(lái)。
對(duì)于復(fù)制初始化,最顯示的調(diào)用手段就是使用“=”符號(hào)(這個(gè)時(shí)候應(yīng)該成為復(fù)制初始化符號(hào))。那么還有許多地方是隱式的調(diào)用。如參數(shù)傳遞時(shí),函數(shù)返回時(shí),初始化容器時(shí)!
1)對(duì)于參數(shù)傳遞:我們知道除非是引用參數(shù),否則就是一個(gè)使用上層對(duì)象復(fù)制初始化函數(shù)參數(shù)的過(guò)程。
2)對(duì)于函數(shù)返回值:我們知道除非是引用返回,否則在return的那個(gè)語(yǔ)句就是使用函數(shù)內(nèi)的對(duì)象,復(fù)制初始化一個(gè)上層對(duì)象(通常是臨時(shí)的,然后馬上有被用于)
3)在某些容器初始化的過(guò)程中如:
? vect<string> svec(5);
???這里的過(guò)程就是,先使用string默認(rèn)構(gòu)造出一個(gè)實(shí)例對(duì)象,然后使用這個(gè)對(duì)象,復(fù)制初始化其它的元素。這個(gè)過(guò)程是容器的實(shí)現(xiàn)細(xì)節(jié),其實(shí)從外面看,可以理解為直接初始化。
4)數(shù)組初始化,有時(shí)候使用這樣的語(yǔ)法:
Sales_item primer_eds[]={ string("1231231"),string("3123123")}
?可知這個(gè)過(guò)程,就是一個(gè)先調(diào)用直接初始化生成string,然后繼續(xù)隱式調(diào)用直接初始化生成Sales_item。最后使用復(fù)制初始化,給那個(gè)數(shù)組的各個(gè)元素初始化。
從上面兩個(gè)關(guān)于容器(包括數(shù)組)的初始化過(guò)程可以看出,他們與普通的類(也是包含許多的元素對(duì)象)的不同了?!
(3)組裝復(fù)制構(gòu)造函數(shù)。
對(duì),我們現(xiàn)在學(xué)習(xí)了,默認(rèn)構(gòu)造函數(shù)(沒有參數(shù)的,可能是系統(tǒng)定義,可能是用戶定義,系統(tǒng)只有在沒有任何構(gòu)造函數(shù)的情況下定義默認(rèn)構(gòu)造函數(shù)),而系統(tǒng)定義的默認(rèn)構(gòu)造函數(shù)就叫做組裝默認(rèn)構(gòu)造函數(shù),還有一種特殊的構(gòu)造函數(shù)——類型轉(zhuǎn)換構(gòu)造函數(shù)(其實(shí)并不特殊);現(xiàn)在學(xué)習(xí)了組裝復(fù)制構(gòu)造函數(shù)(只要用戶沒有主動(dòng)構(gòu)造一個(gè)復(fù)制構(gòu)造函數(shù)(使用類型引用做參數(shù)),系統(tǒng)就會(huì)自行組裝);就算只定義了復(fù)制構(gòu)造函數(shù),系統(tǒng)也不會(huì)自動(dòng)組裝默認(rèn)構(gòu)造函數(shù),所以,如果你定義了復(fù)制構(gòu)造函數(shù),那么一定要定義普通構(gòu)造函數(shù)(最好有默認(rèn)構(gòu)造函數(shù))。要不然,就沒有構(gòu)造函數(shù)了。
想到這里,我們發(fā)現(xiàn)不管什么什么類型的構(gòu)造函數(shù),功能都是實(shí)例并初始化對(duì)象,可能復(fù)制構(gòu)造函數(shù)與普通構(gòu)造函數(shù)的過(guò)程有些不同(其實(shí)就是使用的方法和領(lǐng)域不同),但是他們?nèi)匀皇瞧降鹊摹K灾挥幸粋€(gè)默認(rèn)構(gòu)造函數(shù),并且只有當(dāng)沒有任何自定一構(gòu)造函數(shù)的時(shí)候,系統(tǒng)才會(huì)有組裝構(gòu)造函數(shù);而復(fù)制構(gòu)造函數(shù),是一定有的(不管在什么情況)。
其實(shí)復(fù)制構(gòu)造函數(shù)的本質(zhì)依然是構(gòu)造函數(shù),它的功能就是使用那個(gè)已知的對(duì)象中的元素,“逐個(gè)”的賦值給那個(gè)需要初始化的對(duì)象。所以,你可以把組裝復(fù)制構(gòu)造函數(shù),想象成一個(gè)帶有所有對(duì)象元素(順序也一至)的初始化列表的構(gòu)造函數(shù)。
與普通構(gòu)造函數(shù)一樣,涉及到對(duì)某個(gè)元素的初始化時(shí),對(duì)于內(nèi)建類型,直接使用copy的方法,對(duì)于類類型,使用定義復(fù)制構(gòu)建函數(shù)。如果沒有定義復(fù)制構(gòu)建函數(shù),就使用組裝的,畢竟復(fù)制構(gòu)建函數(shù)的參數(shù)是一定的,所以基本不存在無(wú)法復(fù)制的元素的可能。?但是直接構(gòu)造函數(shù)就有可能出現(xiàn),子元素沒有默認(rèn)構(gòu)造函數(shù)的情況,而不能進(jìn)行構(gòu)造,并且直接構(gòu)造的時(shí)內(nèi)建類型也有可能不初始化(依編譯器而定)。
另外一個(gè)要注意的是,雖然我們沒有辦法進(jìn)行復(fù)制初始化數(shù)組(這就是為什么數(shù)組沒有辦法作為參數(shù)傳遞,或則作為返回值返回,或者定義的時(shí)候用一個(gè)數(shù)組來(lái)復(fù)制初始化另外一個(gè)數(shù)組),但是當(dāng)數(shù)組在某個(gè)其他類型的里面的是后,這個(gè)時(shí)候如果發(fā)生了復(fù)制初始化,作為元素的數(shù)組會(huì)也被復(fù)制初始化,但是,是通過(guò)逐個(gè)復(fù)制元素的方法。
(4)自定義復(fù)制構(gòu)造函數(shù)
class Foo{ public:Foo(); //默認(rèn)構(gòu)造函數(shù)Foo(const Foo&); //復(fù)制構(gòu)造函數(shù) }
其中,const可以不寫(但通常建議這么做),由于復(fù)制的作用要用到傳遞參數(shù)、返回值,這些都是隱式的調(diào)用,所以一定不能將其聲明為explicit。所以可以知道explicit的作用就是承認(rèn)在可能的情況下,系統(tǒng)默認(rèn)在需要使用該函數(shù)的情況下,能不能自動(dòng)使用。
從前面我們說(shuō)可以將組裝復(fù)制構(gòu)造函數(shù)用相應(yīng)的帶有完全初始化列表的構(gòu)造函數(shù)來(lái)代替,我們可以發(fā)現(xiàn),復(fù)制構(gòu)造函數(shù)要實(shí)現(xiàn)的功能基本穩(wěn)定,所以通常組裝復(fù)制構(gòu)造函數(shù)基本可以滿足要求。
所以我們經(jīng)常不怎么自己定義復(fù)制構(gòu)造函數(shù),但是有些時(shí)候程序?qū)崿F(xiàn)要求我們必須自定義構(gòu)造函數(shù),這個(gè)時(shí)候我們就表明了,構(gòu)建復(fù)制構(gòu)造函數(shù)的困難之處,不在于語(yǔ)法?,它與普通的構(gòu)造函數(shù)是一樣的;關(guān)鍵是在,構(gòu)建這個(gè)東西的用途,當(dāng)用途明確后,其他就簡(jiǎn)單了。
有些時(shí)候,例如
1)對(duì)象成員是一個(gè)指向某個(gè)資源的指針(用戶本意不想只是復(fù)制指針,那表明沒有復(fù)制指針指向的對(duì)象),
2)或者該類型規(guī)定每新建一個(gè)對(duì)象需要做一些動(dòng)作,那么這個(gè)時(shí)候就需要自定義復(fù)制構(gòu)造函數(shù)
(注意哦!與普通構(gòu)造函數(shù)一樣,構(gòu)造函數(shù)理論上的功能包括空間分配、元素初始化,以及相關(guān)處理,那個(gè)復(fù)制初始化符號(hào)“=”左右應(yīng)該看成一個(gè)整體。)
3)或者該類型的每個(gè)對(duì)象都富有一個(gè)唯一ID成員的機(jī)制
(5)如何阻止類的復(fù)制初始化功能
1)我們知道,我們使用explicity可以聲明,該復(fù)制構(gòu)造函數(shù)不能被隱式使用,于是在參數(shù)、返回值的那些地方都不能用,但是如何讓普通的復(fù)制初始化也不用呢??那就是聲明為private。我們說(shuō)過(guò)。構(gòu)造函數(shù)的特殊還在于,它是直接被外層使用的,不需要套一個(gè)什么類的帽子(因?yàn)樗褪穷惷?#xff09;,所以如果申明為private那么就不能使用了,這就是為什么大部分的構(gòu)造函數(shù)都被申明為public了。
?
總結(jié)
以上是生活随笔為你收集整理的C++直接初始化和复制初始化的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 致中小保险企业——活下去,活的久,才有更
- 下一篇: 【点阵显示汉字“王”】C++