对象 普通po转_谈谈C++对象的构造
對
象
造化從來自有神
如何對此亦無塵
平生出處皆非妄
老去功名始見真
這是小編以“構(gòu)造對象”為主題讓九歌同學(xué)創(chuàng)作的七言絕句。九歌同學(xué)以一種非常玄妙的文風(fēng)向我們介紹了對象的構(gòu)造,但這似乎并不是我們想要的答案。那么我們每次碼代碼都在進(jìn)行的“對象構(gòu)造”究竟是怎樣一回事呢?就讓小編帶領(lǐng)大家來了解一下吧!
什么是對象的構(gòu)造?
對象的構(gòu)造過程就是對象的創(chuàng)建過程。在對象構(gòu)造的過程中,我們需要進(jìn)行對象數(shù)據(jù)成員的初始化,因此對象構(gòu)造的關(guān)鍵問題就是要解決對象的成員如何初始化的問題。
C++中是如何解決對象成員初始化問題的?
我們可以對照著C語言中結(jié)構(gòu)體數(shù)據(jù)成員初始化的解決方案來看。
假定我們有如下的結(jié)構(gòu)體:
C語言的程序員們有兩種對其進(jìn)行初始化的方法。
其一是順序初始化:
其二是指定初始化:
我們可以注意到,使用順序初始化方法往往不夠靈活,需要逐一列出所有成員的初始值;而指定初始化法不被很多C++編譯器兼容,例如小編使用的Visual Studio就不兼容這種寫法,會報(bào)錯(cuò)。
在C++中,由于成員函數(shù)的引入,我們可以在類中定義一個(gè)成員函數(shù)用來進(jìn)行類的初始化。但這樣會帶來一個(gè)問題,假如在對象被創(chuàng)建之后我們忘記調(diào)用這個(gè)成員函數(shù),那么對象數(shù)據(jù)成員的值將是不確定的,對于一個(gè)大型的工程來說,這可能造成很大的麻煩。這是由于對象的創(chuàng)建以及成員的初始化分離了。此外,對于一些必須進(jìn)行初始化的成員,例如const類型和引用類型的成員,它們的初始化需要和對象同步進(jìn)行,這種先創(chuàng)建對象在初始化成員的做法也會帶來問題。
構(gòu)造函數(shù)——千呼萬喚始出來
?? 構(gòu)造函數(shù)的一般特征
構(gòu)造函數(shù)是一類特殊的成員函數(shù)。相較于一般的成員函數(shù),構(gòu)造函數(shù)有這樣兩個(gè)顯著的特征:
1、所有構(gòu)造函數(shù)都沒有返回類型的聲明;
2、構(gòu)造函數(shù)僅在對象被創(chuàng)建時(shí)被調(diào)用。
我們來看一個(gè)最簡單的構(gòu)造函數(shù)的例子:
在這個(gè)例子中,構(gòu)造函數(shù)被調(diào)用時(shí),會首先創(chuàng)建A這個(gè)對象(此時(shí)a中的數(shù)據(jù)成員尚未被賦值),然后執(zhí)行構(gòu)造函數(shù)的函數(shù)體,將A的數(shù)據(jù)成員a賦值為1,并輸出相關(guān)語句。我們可以觀察到構(gòu)造函數(shù)的一些基本特征:沒有返回類型的聲明,且函數(shù)名稱與類名相同。
多種類型的構(gòu)造函數(shù)
C++中對象被創(chuàng)建的方式是多種多樣的,因此構(gòu)造函數(shù)的形式也是多種多樣的。接下來就讓我們看一看為了完成對象創(chuàng)建這個(gè)任務(wù),都有哪些類型的構(gòu)造函數(shù)吧!
默認(rèn)構(gòu)造函數(shù):
當(dāng)且僅當(dāng)類中沒有定義構(gòu)造函數(shù)時(shí),編譯器會提供一個(gè)默認(rèn)的構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)沒有參數(shù),并且函數(shù)體也是空的。在上面的例子中,這個(gè)構(gòu)造函數(shù)就類似于:Test(){};
無參構(gòu)造函數(shù):
上面講到的默認(rèn)構(gòu)造函數(shù)就是一種無參構(gòu)造函數(shù)。需要注意的是,如果參數(shù)表中的形參均具有默認(rèn)值,此時(shí)我們也可以將它當(dāng)做無參的構(gòu)造函數(shù)來調(diào)用:
有參構(gòu)造函數(shù):
我們通過一個(gè)例子來感受一下有參構(gòu)造函數(shù)的使用以及初始化列表的作用。
在這個(gè)例子中,我們聲明的類Test有三個(gè)數(shù)據(jù)成員,其中有一個(gè)是引用成員,一個(gè)是const類型的成員,這兩種類型的數(shù)據(jù)成員在定義時(shí)就必須進(jìn)行初始化,因此我們不能在構(gòu)造函數(shù)的函數(shù)體內(nèi)將它們初始化,而應(yīng)當(dāng)在初始化列表中將它們初始化,這是因?yàn)樵诔跏蓟斜碇羞M(jìn)行的初始化工作是在對象創(chuàng)建完成之前進(jìn)行的,而在函數(shù)體中進(jìn)行初始化是在對象創(chuàng)建之后進(jìn)行的。
一般來說聲明了帶參數(shù)的構(gòu)造函數(shù)之后我們最好還要定義一個(gè)默認(rèn)構(gòu)造函數(shù),以防止系統(tǒng)編譯時(shí)出錯(cuò)。
拷貝構(gòu)造函數(shù)——我來組成一支大軍!
?為什么需要拷貝構(gòu)造函數(shù)?
對于普通類型的變量而言,變量的復(fù)制是容易的,利用一個(gè)變量來初始化一個(gè)新變量也是容易的:
但正如對象的創(chuàng)建需要構(gòu)造函數(shù)一樣,對象內(nèi)部因?yàn)橛懈鞣N成員變量,其復(fù)制過程也比普通的變量要復(fù)雜,因此通過復(fù)制的方法使用一個(gè)對象去初始化另一個(gè)對象就需要用到拷貝構(gòu)造函數(shù)。
我們看一個(gè)簡單的例子:
在這個(gè)例子中,以“const Test& t”為參數(shù)的函數(shù)就是一個(gè)簡單的拷貝構(gòu)造函數(shù)??截悩?gòu)造函數(shù)的一般特征是:
1、參數(shù)中必須含有本類對象的一個(gè)引用,可以含有其他參數(shù),但這些參數(shù)必須有默認(rèn)值;
2、函數(shù)名與類名相同,且沒有返回類型的聲明。
因?yàn)槭褂昧艘?#xff0c;且拷貝構(gòu)造函數(shù)一般不希望改變被復(fù)制的對象,因此我們往往在這個(gè)參數(shù)前加上“const”修飾符。
拷貝構(gòu)造函數(shù)的調(diào)用時(shí)機(jī)
拷貝構(gòu)造函數(shù)在下3種情況下會被調(diào)用:
1、用類的對象初始化類的另一個(gè)對象:在上面舉出的例子中我們可以看到,當(dāng)執(zhí)行
Test B=A;
時(shí),拷貝構(gòu)造函數(shù)會被調(diào)用。此外,如果我們把這個(gè)語句改寫成:
Test B(A);
拷貝構(gòu)造函數(shù)也會被調(diào)用。這兩種寫法執(zhí)行的操作完全相同。
2、某個(gè)函數(shù)的形參是類的對象,當(dāng)這個(gè)函數(shù)被調(diào)用,進(jìn)行形實(shí)結(jié)合時(shí)也會調(diào)用拷貝構(gòu)造函數(shù):
當(dāng)調(diào)用函數(shù) f(Test a) 時(shí),會調(diào)用Test類的拷貝構(gòu)造函數(shù)。只有參數(shù)使用值傳遞時(shí)才會調(diào)用拷貝構(gòu)造函數(shù),使用引用傳遞則不會,因此在傳遞較大的對象時(shí),一般采用引用傳遞的策略。
3、函數(shù)的返回值是類的對象,在返回值時(shí)會調(diào)用拷貝構(gòu)造函數(shù)。例如:
在這個(gè)例子中,執(zhí)行語句
b=f(A);
時(shí),當(dāng)傳遞參數(shù)時(shí)拷貝構(gòu)造函數(shù)不會被調(diào)用(因?yàn)槭褂玫氖且脗鬟f),但函數(shù)返回對象時(shí)拷貝構(gòu)造函數(shù)會被調(diào)用。
深拷貝和淺拷貝
當(dāng)我們定義一個(gè)類時(shí),系統(tǒng)一般會給出一個(gè)默認(rèn)的拷貝構(gòu)造函數(shù),這個(gè)構(gòu)造函數(shù)可以實(shí)現(xiàn)成員對象簡單的一一復(fù)制。如果成員中沒有動態(tài)成員的話,這個(gè)一一復(fù)制的結(jié)果就是我們想要的,但如果成員中有一個(gè)指針指向了一片動態(tài)開辟的存儲空間,使用系統(tǒng)默認(rèn)的拷貝構(gòu)造函數(shù)就會出現(xiàn)問題:
假設(shè)我們定義的類Test中有一個(gè)指針變量p,且我們先創(chuàng)建了一個(gè)對象A。A中的指針p在對象使用的過程中指向的是一片動態(tài)開辟的存儲空間,那么在我們執(zhí)行語句
Test B=A;
時(shí),B中的指針p也會指向同一片存儲空間。
假設(shè)我們在析構(gòu)函數(shù)的函數(shù)體中試圖釋放這片存儲空間,那么由于B和A中的指針p指向了同一片空間,而同一片空間不可以被釋放兩次,系統(tǒng)就會報(bào)錯(cuò)。(關(guān)于析構(gòu)函數(shù)的內(nèi)容,可以參考文章的下一部分)
同樣的,假如B和A的指針指向的存儲空間是一樣的,那么A.p指向的內(nèi)容改變的話,B.p指向的內(nèi)容也會被改變,這顯然不是我們想要的。
因此我們想要實(shí)現(xiàn)的是,拷貝過程中,A和B中的動態(tài)變量指向的內(nèi)容相同,但指針值本身不同。這就是所謂的“深拷貝”。
我們可以考慮以下的方式來定義一個(gè)深拷貝函數(shù):
在這個(gè)例子中,拷貝構(gòu)造函數(shù)的函數(shù)體使得復(fù)制出來的對象的指針p指向的內(nèi)容與被復(fù)制對象的指針p指向的內(nèi)容一致,但兩者本身指向的存儲空間是不同的,也就實(shí)現(xiàn)了深拷貝。
~析構(gòu)函數(shù)——我有神奇小尾巴~
?初識析構(gòu)函數(shù)
類的析構(gòu)函數(shù),它是類的一個(gè)成員函數(shù),名字由波浪號加類名構(gòu)成,是執(zhí)行與構(gòu)造函數(shù)相反的操作:釋放對象使用的資源,并銷毀非static成員。
析構(gòu)函數(shù)有這樣幾個(gè)特征:
1.函數(shù)名是在類名前加上~,無參數(shù)且無返回值,因此析構(gòu)函數(shù)不能重載。
2.一個(gè)類只能有且有一個(gè)析構(gòu)函數(shù),如果沒有顯式的定義,系統(tǒng)會生成一個(gè)缺省的析構(gòu)函數(shù)。
我們看一個(gè)簡單的析構(gòu)函數(shù)的例子:
在討論構(gòu)造函數(shù)時(shí)我們提到過,構(gòu)造函數(shù)的函數(shù)體是在對象初始化完成之后執(zhí)行的,這也是某些數(shù)據(jù)類型的成員不能通過函數(shù)體進(jìn)行初始化的原因。與構(gòu)造函數(shù)相反,析構(gòu)函數(shù)的函數(shù)體是在對象被銷毀前執(zhí)行的,并且成員的銷毀順序也是與初始化的順序相反的。
釋放資源?
我們知道析構(gòu)函數(shù)的作用是銷毀不再使用的對象(好殘忍),釋放系統(tǒng)資源,那么析構(gòu)函數(shù)是怎么做到這一點(diǎn)的呢?
其實(shí)小編也不知道 如果是對象中非指針類型的成員,那么在執(zhí)行析構(gòu)函數(shù)之后,這些成員占用的內(nèi)存空間會被釋放;但有一類情形是,假如對象中有指針類型的成員,并且它指向的是一塊動態(tài)開辟的存儲空間,那么這塊空間必須由我們手動釋放,也就是說需要在析構(gòu)函數(shù)的函數(shù)體中釋放這塊存儲空間。
小編弱語
關(guān)于對象的構(gòu)造和析構(gòu),其實(shí)還有很多可聊的,但由于小編實(shí)力有限,暫時(shí)只能和大家聊這么多啦!最后,獻(xiàn)上九歌同學(xué)的一首五言絕律詩《你在哪里啊,我的對象》,向大家告別~
你在哪里啊,我的對象?
九歌
一自傷春眼,無人識此心
山林隨日轉(zhuǎn),云雨隔年沉
世路皆新鬼,浮生付亂禽
憑君莫剪伐,吾已負(fù)初音
文字 | 周航
審核|劉政寧
指導(dǎo)老師|李超
總結(jié)
以上是生活随笔為你收集整理的对象 普通po转_谈谈C++对象的构造的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pq 中m函数判断嵌套_Python中n
- 下一篇: mvc开发模式