天天看點

C++學習第三日 構造、析構、指派構造、拷貝構造

一、this 指針

類的成員變量單獨存儲在每個類對象中,成員函數存儲在代碼段中,所有的類對象共享一份成員函數

成員函數是如何差別調用它的是哪個類對象?
答:借助了 this 指針,類的成員函數都有一個隐藏的參數 this 指針,它指向類對象。

類的構造函數中也同樣有this指針,指向的就是正在構造的這個對象。

在類中(成員、構造、析構函數)對成員變量、成員函數的通路都是借助了 this 指針。
this 指針是隐藏的,但也可以顯示使用:
	1、參數與成員名重合時,使用 this 可以差別出成員變量與參數名
	2、在成員函數中如果想傳回目前對象的指針、引用等,可以使用 this 指針實作
	3、将 this 指針作為函數的參數從一個對象傳遞給另一個其他類的對象,可以實作不同對象間的互動。
           

二、常函數

在函數的參數清單與函數體之間有const修飾的函數,這個 const 其實就是在修飾 this 指針。

不能在常函數内修改成員變量的值,普通的成員函數可以調用常函數,而常函數隻能調用常函數。

如果在常函數中真的需要修改某個成員變量的資料,那麼需要這個成員被 mutable 修飾
           

三、析構函數

1、特殊的成員函數
	函數名必須是
		~類名(void)
		{
		
		}
		沒有參數、沒有傳回值、不能重載
2、誰來調用
	析構函數會在銷毀對象時自動調用,在對象的整個生命周期内最多被調用一次。
3、析構函數負責什麼
	析構函數負責釋放在構造函數期間擷取到的所有資源,它的執行過程:
		1、先執行析構函數本身代碼
		2、調用成員類的析構函數
		3、調用父類的析構函數
4、預設的析構函數		
	如果一個類中沒實作析構函數,編譯器會自動生成一個具有析構函數功能的二進制指令,它負責釋放編譯器能夠看的到的資源(成員變量、類成員,父類成員),這就是預設析構。
	如果一個類沒有動态資源,也不需要做善後工作,預設析構就完全夠用了,不需要在定義新的析構函數。
	注意:預設析構無法釋放動态資源(堆記憶體)。
	
	作業:類對象的建立過程與釋放過程
		建立過程:配置設定記憶體(對象)->父類構造->成員構造->自己的構造
			父類構造:按照繼承表從左至右依次構造
			成員構造:按照聲明順序從上到下依次構造。
		釋放:自己析構->析構成員->析構父類->釋放記憶體(對象)
			析構成員:按照聲明順序從下到上依次構造。
			析構父類:按照繼承表從右至左依次構造
           

四、拷貝構造

拷貝構造又稱為複制構造,是一種特殊的構造函數,它是使用一個現有的舊對象構造一個新的對象時調用的函數,隻有一個引用型的參數(對象本身)。
類名(類&)
{

}
拷貝構造的參數應該加 const 保護,但編譯器并沒有強行限制。
編譯器會自動生成一個拷貝構造函數,它負責把舊對象中的所有資料拷貝給新建立的對象

深拷貝與淺拷貝的差別:
	如果類成員中有指針,淺拷貝隻拷貝指針變量的值,而深拷貝是拷貝指針變量所指的目标
	
什麼情況下需要實作拷貝構造:
	當類成員中有指針成員,此時預設的拷貝構造(淺拷貝)就無法完成任務,需要自己動手實作拷貝構造(深拷貝)。
	
什麼情況下會調用拷貝構造:
	1、使用一個舊對象給新對象指派時
	User user1 = user;
	2、使用對象當作函數的參數,當調用函數時就會一起調用拷貝構造
           

五、指派構造(指派運算符)

當一個舊類對象給另一個舊類對象指派時,就會調用指派構造
void opeator = (類&)
{

}
什麼時候會調用:
	對象 = 對象;

編譯器會生成一個預設的指派構造它負責把一個對象的記憶體拷貝給另一個對象

什麼情況需要實作指派構造:
	當深拷貝時需要自己動手實作指派構造,也就是拷貝構造與指派構造需要同時實作。
	
編譯器會自動生成四個成員函數:構造、析構、指派構造、拷貝構造	
           

六、關于拷貝構造、指派構造的建議

1、預設的拷貝構造、指派構造函數不光會拷貝本類的資料,也會調用成員類對象和父類的拷貝構造和指派構造,而不是單純的按位元組複制,是以盡量少使用指針成員。
2、在函數參數中盡量使用類指針或引用來當參數(不要直接使用類對象),減少調用拷貝構造和指派構造的機會,也可以降低資料傳遞的開銷
3、如果由于特殊原因無法實作完整的拷貝構造、指派構造,建議将它們私有化,防止誤用。
4、一旦為一個類實作了拷貝構造,那麼也一定要實作指派構造。	
           

七、靜态成員

類成員一旦被 static 修飾就會變成靜态成員,而是單獨一份存儲在bss或data記憶體段中,所有的類對象共享(靜态成員屬于類,而不屬于某個對象)。
靜态成員類内聲明,但必須在類外定義、初始化,與成員函數一樣需要"加類::"限定符表示它屬于哪個類,但不需要再額外加 static。
成員函數前也可以被 static 修飾,這種函數叫靜态成員函數,這種成員函數沒有 this 指針,是以在靜态函數中不能直接通路類的成員變量、成員函數,但可以直接通路靜态成員變量、靜态成員函數。
靜态成員變量、函數依然受通路控制限定符的影響。
因為在代碼編譯完成後靜态成員已經定義完成(有了存儲空間),是以可以不通過類對象而直接調用,類名::靜态成員名

普通成員函數中可以直接通路靜态成員變量、靜态成員函數。

靜态成員變量可以被當作全局變量來使用(通路限制必須得是 public),靜态成員函數可以當作一個類的接口,實作對類的管理
           

八、單例模式

什麼是單例模式:隻能建立出一個類對象(隻有一個實體的執行個體)的叫單例模式	
單例模式的應用場景:
	Window系統的任務管理器
	Linux/Unix系統的日志系統
	網站的通路計數器
	服務端程式的連結池、線程池、資料池
擷取單一對象的方法
	1、定義全局(C語言),但不受控制,防君子不防小人
	2、專門寫一個類,把類的構造函數設定成私有,借助靜态成員函數提供一個接口以此來擷取唯一的執行個體。
	
C++如何實作單例模式:
	1、禁止類的外部建立類對象:構造函數設定私有
	2、類自己維護一個唯一的對象:使用靜态指針指向
	3、提供一個擷取執行個體的方法:靜态成員函數擷取靜态指針
	
餓漢模式:
	将單例類的唯一執行個體對象定義為成員變量,當程式開始運作,執行個體對象就已經建立完成。
	優點:加載進行時靜态建立單例對象,線程安全。
	缺點:無論使用與否,總要建立,浪費記憶體。

懶漢模式:
	用靜态成員指針來指向單例類的唯一執行個體對象,隻有真正調用擷取執行個體對象的靜态接口時,執行個體對象才被建立。
	優點:什麼時候用什麼時候建立,節約記憶體
	缺點:在第一次調用通路擷取執行個體對象的靜态接口時才真正建立,有可能建立多個對象,如果在多線程操作情況下有可能被建立出多個執行個體對象(雖然可能性很低),存線上程不安全問題。
           

繼續閱讀