天天看點

C++語言(03)——對象的構造對象的構造(上)對象的構造(中)對象的構造(下)初始化清單的使用對象的構造順序對象的銷毀神秘的臨時對象二階構造模式對象的構造順序

(1)從程式設計的角度來看,對象隻是變量,定義對象就是定義變量,是以:

在棧上建立對象時,成員變量初始值為随機值

在堆上建立對象時,成員變量初始值為随機值

在靜态資料區上建立對象時,成員變量初始值為0

(2)全局變量和static修飾的局部變量存儲在靜态資料區,沒有顯式初始化其值為0(bss/ZI段)

(1)生活中的對象都是初始化之後上市的,如手機,電腦等,我們希望程式中的對象也可以初始化為固定值

(1)在類中提供一個public的initialize函數

(2)函數中手工對類的成員進行顯式初始化

(3)對象建立後需要立即調用initialize函數進行初始化

(4)initialize函數隻是一個普通函數,如果未及時調用,運作結果是不确定的

(1)C++中可以定義與類同名的構造函數

(2)構造函數:與類同名、沒有任何的傳回值類型、在對象定義時會被自動調用

(1)構造函數可以根據需要定義參數

(2)一個類中可以存在多個重載的構造函數構造函數的重載遵循C++重載的規則

(3)構造函數在對象定義時會被自動調用,此外我們可以手工調用構造函數

注意:

對象的聲明和定義不同,對象定義:申請對象的空間并調用構造函數

對象聲明:告訴編譯器有這樣一個對象

(1)就是沒有參數的構造函數

(2)當類中沒有定義構造函數時(拷貝構造函數也是構造函數),編譯器會預設提供一個無參構造函數,其函數體為空

(1)參數為const class_name&的構造函數

(2)當類中沒有定義拷貝構造函數時,編譯器會預設提供一個拷貝構造函數,簡單的進行成員變量的複制(淺拷貝)

(3)拷貝構造函數的意義:相容C語言的初始化方式(使用變量為其他變量指派),使用已建立的對象為其他對象指派

(1)淺拷貝:拷貝後的實體狀态相同

(2)深拷貝:拷貝後的邏輯狀态相同

(3)編譯器預設提供的拷貝構造函數隻進行淺拷貝

什麼時候需要深拷貝?

對象中有成員使用了系統資源(成員指向了動态記憶體空間、成員打開了外存中的檔案、成員使用了系統中的網絡端口)

(1)調用淺拷貝構造函數進行初始化,初始化的兩個變量不但參數相同,而且共用同一塊記憶體,在兩次釋放記憶體時就會出錯

(2)工程中自定義拷貝構造函數時,必然要實作深拷貝(為新的對象重新配置設定資源)

(1)在類中可以定義const成員,const成員會被配置設定空間,存儲位置取決于其對象定義在哪裡

(2)類中的const是隻讀變量

(3)在類中不能直接對const成員進行初始化,隻能在初始化清單中指定初始值

(1)C++中提供了初始化清單對成員進行初始化

(2)文法規則

ClassName::ClassName() //構造函數

: m1(v1),m2(v2, v3),m3(v1) //初始化清單

{

//構造函數函數體

}

(3)注意事項:

成員的初始化順序與成員的聲明順序相同,與初始化清單中的順序無關

初始化清單先于構造函數的函數體執行

(1)局部變量的構造順序依賴于程式的執行流,是以開發中要避免使用goto語句)(破壞程式的執行流)

(2)堆對象的構造順序依賴于new的使用順序

(3)全局對象的構造順序是不确定的,不同的編譯器使用不同的規則确定構造順序,是以要盡量避免全局對象

(1)生活中對象都是初始化後才上市的,對象被銷毀前會做一些清理工作

方案1:

提供一個public的free函數,

(1)當對象不再需要時立即調用free函數進行清理

(2)free隻是一個普通的函數,必須顯示的調用

(3)對象銷毀之前沒有做清理,很可能造成資源洩漏

方案2:

析構函數

(1)C++中可以定義一個特殊的清理函數,析構函數,功能和構造函數相反

(2)析構函數在對象銷毀時被自動調用

(3)析構函數沒有傳回值也沒有參數(表明析構函數在一個類中是唯一的,不可能重載)

(4)文法:

~ClassName()

(5)一般當類中自定義了構造函數,并且函數中使用了系統資源,則需要定義析構函數,釋放系統資源,防止記憶體洩漏

(1)直接調用構造函數将産生一個臨時對象,臨時對象的生命周期隻有一條語句的時間,臨時對象的作用域隻在一條語句中

(2)臨時對象是C++中值得警惕的灰色地帶,是性能的瓶頸,也是bug的來源之一

(3)實際工程開發中需要人為的避開臨時對象

(4)現代C++編譯器會盡力避開臨時對象

思考:如何解決構造函數的代碼複用問題?

方案是提供一個private的init函數,然後在構造函數中去調用它.

回顧構造函數的特點:

與類同名,沒有傳回值,在對象建立時被動調用,用于對象的初始化

1、如何判斷構造函數的執行結果?

一般來說無法判斷。但是我們可以人為的類中定義一個用于表明構造函數執行結果的變量,并在構造函數結束的地方給該變量指派,最後通過讀取該變量的值來得知構造函數的執行結果

2、在構造函數中執行return語句會發生什麼?

首先在構造函數中指向他return是合法的,執行return語句後構造函數立即結束

3、構造函數執行結束是否意味着對象構造成功?

構造函數隻提供自動初始化成員變量的機會,不能保證初始化邏輯一定成功。構造函數決定的是對象的初始化狀态,而不是對象的誕生。

也就是說構造函數初始化操作的失敗不影響對象的誕生

初始化操作不能按照預期完成而得到的對象

是C++中的合法對象,也是bug的來源

工程開發中的構造過程可分為:

第一階段構造:(真正的構造函數)

資源無關的初始化操作,不可能出現異常的操作

第二階段構造:(傳回值表示初始化狀态的普通函數)

需要時用系統資源的操做,可能出現異常情況(記憶體申請,通路檔案)

如圖所示:(27-2)

C++語言(03)——對象的構造對象的構造(上)對象的構造(中)對象的構造(下)初始化清單的使用對象的構造順序對象的銷毀神秘的臨時對象二階構造模式對象的構造順序

總結:

(1)二階構造認為的将初始化分為兩部分,能夠確定建立的對象都是完整的

(2)二階構造的構造函數都是私有的,并提供了一個用于建立對象的靜态函數指針???(通過類名直接通路,然後建立對象),是以最終的對象配置設定在堆區

(3)實際工程中需要初始化的資料都是比較多的,是以對象建立在堆區是合理的

使用二階構造完善之前的數組類

析構函數的調用順序

析構函數與對應的構造函數的調用順序相反,是以我們之隻要知道構造函數的調用順序就可以知道析構的順序

(1)單個函數建立時構造函數的調用順序(先父母,後他人,再自己)

1、調用父類的構造過程

2、調用成員變量的構造函數

3、調用類自身的構造函數

(2)對于棧對象和全局對象,類似于入棧和出棧的順序,最先構造的對象最後被析構

(3)堆對象的析構發生在使用delete的時候,與delete的使用順序相關

繼續閱讀