背景開發核心技術與應用實踐讀書筆記(二)
第2章 面向對象的C++
2.1 類與對象
- 概念
- 成員函數
-
封裝性
把資料和資料相關的操作封裝在類裡,隻對可信的類或對象開放
- 構造函數
- 資料成員不能再類中初始化必須在構造函數中
- 類中可以定義不同的構造函數以提供不同的初始化方式(重載)
- 注意要在聲明構造函數參數時指定預設參數值,而不是在定義中
- 如果定義了全是預設參數的構造函數,則不能定義重載構造函數
- 析構函數
- 函數中定義一個對象,函數調用完成時,對象會被釋放
- static局部對象隻在main與exit函數結束程式時,才會調用析構函數
- 全局對象與2相同
- new 建立的對象,delete時會調用
- 靜态資料成員
- 可以把資料當成全局變量使用,但是又被隐藏在類的内部——使用靜态資料成員
- 類的靜态成員擁有一個存儲區,多個對象共享這一塊靜态存儲區,如果改變,則各個對象的這個資料成員的值都被改變了
- 靜态資料成員類外初始化,并且不随類對象的建立與消失而改變
- 它在編譯期間被配置設定空間,在程式結束釋放
- 可以通過對象名來引用也可以通過類名來引用
- 靜态成員函數
- 通過**類名::**和對象來調用
- 它的出現隻是為了處理靜态資料成員,直接引用該靜态成員函數
- 靜态成員函數沒有 this指針,故不能通路本類的非靜态成員
- 如果一定要引用該類的非靜态成員函數,應加對象名,例如a.width;
- 對象的存儲空間
- 空類占1個位元組
- 靜态資料成員、成員函數、構造/析構函數不占空間
- 如果一個類隻有虛函數,則相當于含有一個指向虛函數的指針,64位8byte
- 函數代碼是存儲在對象空間之外的,而且函數代碼共用
- this 指針
- 成員函數都包含的指針,指向被調用成員函數所在的對象
- 在成員函數開始前構造,在成員函數結束後清除,是以不能通過對象來使用this 指針
- 會應編譯器的不同而有不同的存儲位置,可能是棧、寄存器或全局變量區
- 普通類函數都不會建立一個函數表來儲存函數指針,隻有虛函數放到函數表中
- 類模闆
- 多個類功能相同,資料類型不同
- 類外定義成員函數因寫成:
template <class T>; T Operation<T>::add(){ return x+y;}
- 函數模闆是編譯時自動生 成各種類型的函數執行個體,如同内聯函數,編譯時其實作必須可見,故一般其實作都必須在頭檔案中
- 析構函數與構造函數的順序
- 全局對象,構造函數在所有函數之前調用(包括main);不同檔案都有全局對象,則構造順序不可知。并且在main結束或exit調用析構函數
- 局部對象, 局部構造與析構,多次調用多次構造析構
- 函數中有靜态局部對象,隻在程式第一次調用該函數時構造,結束也不析構,隻有在main或者exit函數結束析構
2.2 繼承與派生
-
繼承性
可以讓某個類型的對象獲得另一個類型的對象的屬性的方法。即子類可以獲得父類的屬性和方法,實作了代碼重用
- 派生類的通路屬性
- 公有繼承,基類的共用成員和保護成員保持原有屬性,私有成員基類私有
- 保護繼承,基類的共用成員和保護成員在子類中變成了保護成員,類外不可見,私有成員基類私有
- 私有繼承,共有成員與保護成員在子類中變成私有成員,類内使用,私有成員同上面1、2
-
派生類的構造函數與析構函數
對派生類的構造函數有以下說明:
- 基類成員與**子對象成員(指的是資料成員為一個類對象)**必須初始化清單中初始化
- 派生類的構造順序:基類構造函數->子對象構造函數->派生類構造函數體
- 有多個基類時;調用順序按照定義派生類時生命的順序(自左向右),與初始化清單無關
- 如果派生類的基類也是派生的,隻需負責直接基類的構造,依次往上
- 派生類有多個子對象成員,派生時按照聲明的順序(從前往後)
- 基類的構造函數定義了一個與多個參數時,派生類必須定義構造函數
- 基類中定義了預設構造函數或沒有定義構造函數,派生類可以省略對基類構造函數的調用(<基類名>(<參數表>)),子對象成員也相同
- 所有基類或子對象的構造函數都可以省略時,可以省略基類構造函數的調用
- 基類與子對象構造函數都不要參數,派生類參數也不需要時,派生類構造函數可以不定義
- 不繼承基類的析構函數,但是需要通過派生類的析構函數去調用基類的析構函數(系統來完成的)
- 可根據自己的需要定義自己的析構函數對增加成員進行清理
- 在執行派生類的析構函數時,系統會自動調用基類和子對象的析構函數;
- 派生類的構造函數與析構函數的額調用順序
- 構造函數的調用順序上一小節以給出
- 析構函數的調用順序與構造函數正好相反
- 析構函數在下列3種情況下被調用
- 對象生命周期結束時
- delete該對象的指針,或指向對象基類類型的指針(其基類析構函數必須是虛函數),這裡就是為什麼基類的析構函數設定為虛函數的原因;
- 對象i是o的成員,O被析構,i也會被析構
2.3 類的多态
-
多态
多态性是指具有不同功能的函數可以使用一個函數名,在面向對象方法中:向不同的對象發送同一個消息,不同的對象在接受時會有不同的行為(即執行不同的函數)。主要展現在子類對父類函數的重寫
注意:
- 派生類重新定義此函數,要求函數名、函數類型、參數個數與類型全部與基類虛函數一緻
- 定義一個指向基類對象的指針,并将它指向同一類族的對象,由此可以調用相應類中的函數
- 非虛函數在子類重新定義,則基類指針調用此函數則調用的是基類的成員函數,若是派生類指針則調用派生類的函數,這不是多态行為。
-
虛函數的使用
使用虛函數時,系統具有一定的空間開銷。當一個類帶有虛函數時,編譯系統會為之配置設定一個虛指針。系統在動态關聯時的時間開銷是很少的。是以多态是高效的
- 純虛函數
- 基類沒有定義,原型後加“=0”,這是因為基類本身生成的對象是不合理的,比如動物這個基類
- 子類一定要實作,否則編譯出錯
- 析構函數
- 構造函數不能聲明為虛函數原因
- 編譯器構造對象在構造時必須知道确切類型
- 構造之前,對象不存在,無法使用指向此對象的指針來調用構造函數
-
析構函數聲明為虛函數原因
C++明确指出**:子類對象由父類指針删除,而基類即析構函數是非虛的,會導緻對象的子類成分沒有析構掉,這樣容易引發記憶體洩漏**
- 構造函數不能聲明為虛函數原因
-
單例模式
單例模式的要點有三個:
- 單例類有且僅有一個執行個體
- 單例類必須自行建立自己的唯一執行個體
- 單例類必須給所有其他對象提供這一執行個體
線程安全單例模式class CSingleton { private: CSingleton() //構造函數是私有的 { } static CSingleton *m_pInstance; public: static CSingleton * GetInstance() { if(m_pInstance == NULL) //判斷是否第一次調用 m_pInstance = new CSingleton(); return m_pInstance; } };
// 線程安全的單例模式 class Singleton { private: Singleton() { } ~Singleton() { }cpp Singleton(const Singleton &); Singleton & operator = (const Singleton &); public: static Singleton & GetInstance() { static Singleton instance; return instance; } };
索引
- 第 1 章 C++常用的程式設計技術
- 第 2 章 面向對象C++
- 第 3 章 常用的STL使用
- 第 4 章 編譯
- 第 5 章 調試
- 第 6 章 TCP協定
- 第 7 章 網絡IO模型
- 第 8 章 網絡分析工具
- 第 9 章 多線程
- 第 10 章 程序
- 第 11 章 程序間通信
- 第 12 章 HTTP協定