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