標籤:效率 實現 rtu nbsp 初始化 自動 替換 函數指標 虛函數
C++中多態的實現原理
當類中聲明虛函數時,編譯器會在類中產生一個虛函數表
虛函數表是一個儲存類成員函數指標的資料結構
虛函數表是由編譯器自動產生與維護的
virtual成員函數會被編譯器放入虛函數表中
存在虛函數時,每個對象中都有一個指向虛函數表的指標(vptr指標)
C++記憶體模型中,會將對象中的成員變數和成員方法分開儲存,同時在成員函數中第一個參數的位置添加一個this指標。
類對象中的普通成員變數,struct變數具有相同的記憶體布局和位元組對齊。
類對象中的靜態成員變數,儲存在全域資料區(我也不知道存在記憶體的什麼位置,不知道是不是和那些常量以及字串儲存在同一位置)
類中的成員方法,儲存在程式碼片段中,同時在靜態聯編的時候,會將同一個類中的函數重載進行函數名稱的替換(具體方式應該是函數名稱+參數類型)。
但是,如果一個類中加了virtual關鍵字,C++編譯器會有另外一種方式來進行上面描述的安排,會在struct中添加一個vptr指標(這就是為什麼在使用virtual關鍵字之後在使用sizeof操作符會多4個位元組的原因)
如果一個類中使用virtual關鍵字,在用類定義對象的時候,會在對象中添加一個vptr指標屬性(這個操作是編譯器自動進行的,對於程式員是不可見的),
C++將虛函數做成虛函數表,儲存了多個虛函數的入口地址,然後將對應的虛函數指標放置到虛函數表中。
C++編譯器不需要區分是子類對象還是父類對象,區分這個函數是虛函數還是普通函數,如果是普通函數就進行普通函數的調用(如果不是虛函數,那就表明是靜態聯編)。
父類對象中有一個vptr指標,子類中也有一個vptr指標,在確定一個函數是虛函數的時候,根據vptr指標找虛函數表,父類有個虛函數表,子類也有一個虛函數表,然後再找函數的入口地址,就實現了一個遲綁定的效果,也就是只有在啟動並執行時候c++編譯器採取判斷。
建構函式中調用虛函數,能實現多態嗎?
vptr指標的分布初始化:
初始化子類的vptr指標是分布完成的。當直行父類的建構函式時(往往是先直行父類的建構函式在執行子類的建構函式),這個時候vptr指標指向的是父類的虛函數表,然後當直行子類的建構函式的時候,vptr指標指向的是子類的虛函數表,最後才完成vptr指標的初始化。
是否可將類函數中的每一個普通函數都聲明為虛函數?可以,但是會影響到使用的效率不建議這麼做。
通過虛函數表vptr確定調用的函數是在啟動並執行時候直行的,因此需要通過定址操作才可以找到真正應該調用的函數,而普通函數在編譯階段就可以確定要調用的函數,因此在效率上而言,虛函數需要定址這一步,因此會慢一點。
說這麼多,為的是說明C++編譯器其實並沒有區分是子類對象還是父類對象,而是通過vptr的形式進行區分的。
多態的理解
效果:同樣一個調用語句,有不同的調用形態,
實現條件:繼承,有virtual重寫,有父類指標指向子類對象
多態實現的理論基礎:動態聯編和靜態聯編,根據實際物件類型來調用重寫的函數。
多態的重要意義是設計模式的基礎,是架構的基石。
函數指標做函數參數,多態的原理也是函數指標做函數參數,實現一個回調的效果。
多態的原理具體實現。
子類指標步長和父類指標步長是不同的概念
多態時用父類指標指向子類對象,和將父類指著進行++操作是兩個不同的概念,++操作每次跳過的記憶體塊大小是兩個不同的概念。
純虛函數和抽象類別
純虛函數是在一個基類中說明的虛函數,在基類中沒有定義,要求任何繼承的類多有自己的版本。純虛函數為各個衍生類別定義了一個公用介面,
純虛函數的類型 virtual 類型 函數名(參數列表)=0
一個具有純虛函數的類成為抽象類別(這個概念和java中的差不多)
純虛函數只定義,沒有函數體,就是為了讓衍生類別來繼承。
虛函數類是不能建立對象的,不過可以建立對象指標,這個就是多態啊。
抽象類別不能作為傳回型別,抽象類別不能作為參數,不過可以聲明抽象類別的引用。
最根本的原因在於,抽象類別不能進行執行個體化,不過由於C++編譯器支援多態,因此,可以作為函數傳回值或者是函數參數。
C++中多態