C 類的生存週期

2021-09-12 14:59:31 字數 4558 閱讀 7324

眾所周知,類是c++的核心,而對於類的生存週期的長短是受到很多因素影響,在這裡我從以下幾個方面來分析:

類在.data段生成時的生存週期

類在棧區生成時的生存週期

類在堆區生成時的生存週期

為了體現各種情況下類的生存週期,我們寫乙個測試用例來直觀地感受。

class cgoods//實現三種構造方式 乙個拷貝建構函式 乙個賦值運算子過載

cgoods()

cgoods(float price)

cgoods(const cgoods& rhs)

/*const

1.防止實參被修改

2.接收隱式生成的臨時量

*/ cgoods& operator=(const cgoods& rhs)

return *this;

} ~cgoods()

private:

char* mname;

int mamount;

float mprice;

};/*

隱式 系統推演物件型別 生成物件

顯式 指出物件型別 生成物件

臨時量內建型別生成的臨時量 ==> 常量

自定義型別生成的臨時量 ==> 變數

隱式生成的臨時量 ==> 常量

臨時物件的優化

條件臨時物件的生成是為了生成新物件

方式以生成臨時物件的方式生成新物件

引用能提公升臨時物件的生存週期

把臨時物件提公升成和引用變數相同的生存週期

*/cgoods good1("good1", 1, 1.1);//.data

int main()

cgoods good2("good2", 2, 2.2);//.data

在這個測試用例中,盡可能的將類在各種情況下出現的可能性都進行實現

執行後的結果如下:

如結果所示,記憶體位置在010段的三個類屬於.data段生成的,他們的位址和堆疊所處的位址位置有很大的不同,這個涉及到c語言的4g虛擬記憶體對於.data段,.text段,.bss段和堆區棧區的劃分,這個在之後的博文中會有提及。目前我們先看.data段生成的類的生存週期。

可以看出010段位址的三個類雖然生成時不在一起,但是在析構時,也就是在生命週期結束時是一起的,而且是在檔案結束時才會進行析構,至於為什麼不是一起生成的,是因為這三個類中有兩個類是全域性類,宣告在main函式外,雖然good2是在main函式的後面,但是程式中編譯連線時會進行位址重定位,保證執行時都是按main函式開始的,在這個同時,所有的全域性變數都會提前在主函式前編譯好,可以說在main函式開始之前,全域性類就已經開始構造了,並且因為沒有加static宣告,這兩個類的鏈結屬性是外部的,有被其他檔案使用的風險,這個需要注意

另乙個則是在main函式裡生成的,因為有static宣告,編譯器將他的生存週期提公升到和全域性類相同,所以在這句語句執行的那個時刻,將其放到了.data段,也就是我這裡的010段記憶體。關於static的相關介紹會在以後的博文中提到,會持續更新本文。

總結:類處於.data段時,他的生存週期就是從宣告的那一刻起,直到檔案結束時才會結束,生存週期是最長的一種。

棧區中的資料是由作業系統來進行管理的,反映到原始檔中就是main函式中的宣告的資料,如區域性變數,區域性類等,並且棧區資料符合棧的filo的特點,在測試用例中,good3至good8(除good6),pgood11,rgood12和rgood13都是屬於棧區資料,但這些類所呼叫的函式卻有些不同。下面逐語句進行分析:

good3最先在main函式宣告時呼叫無引數的建構函式,在main函式結束時最後析構退出。

good4在main函式宣告時呼叫乙個float引數的建構函式,在main函式結束時在good3之前析構退出。

good5在main函式宣告時呼叫char*,int和float引數的建構函式,在main函式結束時在good4之前析構退出。

good3 = 17.5f時,系統進行隱式呼叫,系統推演出物件型別來生成物件,也就是說,系統此時會在cgood類中尋找是否存在乙個float引數的建構函式,如果查詢到,則會以生成臨時物件的方式生成乙個物件(這句話有些繞口,前者是動作,後者是結果),此時這個臨時類呼叫了乙個float引數,此時等號左右都是cgood型別,則呼叫了賦值運算子過載函式,即006ffba4段,進行賦值操作,由於是以生成臨時物件的方式生成的物件,這個物件的生存週期是在一條語句結束的時候(; ?等)結束,即在進行賦值操作結束後本語句結束後進行析構,在執行結果上也是如此。

good3 = cgoods(17.5f);時,屬於顯式呼叫,程式設計師指出物件型別,因為等號左右都是cgood型別,所以呼叫了賦值運算子過載函式,由系統以生成臨時物件的方式生成乙個物件,方式和上條隱式呼叫相同。

good3 = (cgoods)(「good3」, 3, 3.3);時,這是乙個陷阱,很多程式設計師會忽略掉最基礎的乙個運算子——逗號運算子,逗號運算子的作用是在其並列的幾個資料中尋找最後乙個作為最後的賦值物件,所以這句等同於good3 = cgoods(3.3);其中臨時物件的生命週期也是語句結束時結束。

cgoods good7 = 17.5f;

cgoods good8 = cgoods(「good8」, 8, 8.8);這兩句則是標準的隱式呼叫和顯示呼叫,實際上並沒有出現臨時物件,可以認為構造時是給左值進行了構造,由結果也可以看出,他的生命週期實在main函式結束時結束。

cgoods* pgood11 = &cgoods(「good11」, 11, 11.11);此語句宣告乙個cgood型別的指標,儲存的是cgood型別的某乙個位址,可以看出右值並沒有出現物件名,此時編譯器的內部優化起作用,生成乙個臨時量來對宣告的指標進行賦值操作,在賦值之後,本臨時量的生命週期結束,被系統**,其所在的位址也回到了系統手中,此時pgood11所指的記憶體將「不復存在」,也就是說pgood11是沒有任何作用的,且有出錯的風險,但是因為語句的正確性,我們的編譯器放了他一馬,我們在編寫程式的時候一定要避免這種錯誤。

cgoods& rgood12 = cgoods(「good12」, 12, 12.12);我們知道,引用本身是不會生成新的資料,引用在底層實現也是以指標的形式實現的,同時引用也可以提公升被引用的物件或者變數的生命週期至引用變數的生命週期。

這句語句在編譯器認為就是:宣告乙個引用,他所指向的記憶體位址就是乙個沒有名字的cgood型別的物件位址,編譯器的內部優化使得右值產生乙個臨時物件,同時因為引用的作用,這個臨時量的生命週期被提公升到main函式結束,通過最後的執行結果來看,也確實如此。

const cgoods& rgood13 = 17.5f;此語句需要一些推敲。const的作用是告訴編譯器我所指向的資料不會在進行修改,一般用來防止呼叫一些不能修改的值時修改的誤操作,const的一些操作在之後的博文會有提到,這裡我們需要知道,此時的const作用是告訴編譯器這個rgood13引用的不是17.5f(即使你想這樣引用也會是錯誤的,因為引用不可以引用乙個常亮),而是引用乙個指向17.5f的臨時變數,這樣的操作都屬於編譯器的內部優化,他會推測你的想法,雖然大部分情況編譯器總是猜不出你的意思(正確的程式設計風格才是硬道理)同時也因為有const的存在,讓常引用變成合理的存在,又根據引用可以提公升生命週期的屬性,這個臨時量的生命週期被提公升到main函式結束,通過最後的執行結果來看,也確實如此。

cgoods* pgood9 = new cgoods(「good9」, 9, 9.9);//堆區

cgoods* pgood10 = new cgoods[2];//堆區

這兩句由於是new申請的方式生成的物件,所以他們是在堆區生成,由程式設計師進行管理,希望釋放時需要程式設計師顯式的delete,他們的生命週期在new時進行構造,在delete時進行析構結束生命週期,**需要注意的是,當申請的是物件陣列時,並非一次性全部構造好,系統會對每個物件逐個進行構造。**析構時也是逐個進行析構,保證每個物件都可以初始化,每個物件都能被清理掉。

在程式編寫的時候,如果成員變數涉及到指標,指向堆記憶體的情況,那麼在清理記憶體的時候就不能只使用系統提供的預設析構函式,需要將指向堆記憶體的資料手動清理,以免出現記憶體洩漏的問題、

耐著性子看完博文的各位辛苦了,想必大家也發現了,雖然我提到了類在三種情況下的生存週期,但是臨時物件的生命週期卻是最難分析的,他的生命週期會根據前置的操作符改變而改變,這是乙個不確定的因素,也是乙個切入點,很多程式設計的方法都可以根據需要來調整物件的生命週期,這對於各位程式設計師也是乙個機遇吧。

隱式呼叫

系統推演物件型別 生成物件

顯式呼叫

指出物件型別 生成物件

臨時量 內建型別生成的臨時量 ==> 常量

自定義型別生成的臨時量 ==> 變數

隱式生成的臨時量 ==> 常量 (必要時需要新增const)

臨時物件的優化

條件 臨時物件的生成是為了生成新物件

方式 以生成臨時物件的方式生成新物件

引用和指標都能提公升臨時物件的生存週期

把臨時物件提公升成和引用變數相同的生存週期

c 類物件的生存週期

在這裡我們會通過乙個例子來給大家解釋說明 include using namespace std class test public test int a 5,int b 5 ma a mb b cout 1 因為物件t1處於全域性中,所以t1最先被構造,呼叫建構函式 2 由於物件t5 也處於全域性...

C 中物件的生存週期

在c中有變數的生存週期,那麼在c 中就有物件的生存週期。1 普通物件的生存週期 若有乙個cobject類,屬性是姓名和年齡。cobject gobject1 int main cobject gobject2 函式呼叫的先後順序是 建構函式 gobject1 gobject2 object1 obj...

C 變數生存週期演示

能正確寫出下面 的輸出內容嗎?正確的輸出資訊是 d 全域性對像d初始化 a 函式內區域性對像a初始化 b 函式內區域性對像b初始化 b 函式內區域性對像b刪除.以前一直以為給一條 無故加對花括號是無聊的,或者編譯器會把它乾掉,無聊嗎?c 函式內靜態對像c初始化.注意區域性靜態變數雖然生存週期是整個程...