C++對象模式
C++中的成員:
- 成員變量:
靜態(tài)變量
、非靜態(tài)變量
- 成員函數(shù):
靜態(tài)函數(shù)
绍坝、非靜態(tài)函數(shù)
徘意、虛函數(shù)
1. 簡單對象模型
- 對象中只存放指向成員的指針,這么做可以避免成員不同類型轩褐,不同存儲空間的尷尬
- 對象所占內(nèi)存大小為
指針大小 * 成員數(shù)量(成員函數(shù) + 成員變量)
2. 表格驅(qū)動對象模型
- 對象本身只有兩個指向表格的指針椎咧,
成員變量表
和成員函數(shù)表
-
成員變量表
直接存儲變量本身 -
成員函數(shù)表
存儲函數(shù)指針 - 這種理念是虛函數(shù)表得雛形
3. C++對象模型
- C++對象模型是從簡單對象模型派生而來的,并對內(nèi)存空間和存取時間做了優(yōu)化
- 對象本身只有
變量
和一個虛指針
-
虛指針(vptr)
指向虛函數(shù)表(vtbl)
-
變量
:只有非靜態(tài)成員變量
存儲在對象內(nèi)存中把介,其他靜態(tài)成員變量
和所有成員函數(shù)(包括靜態(tài)和非靜態(tài)的)
都在對象內(nèi)存之外 - 注意:C++對象的第一個字節(jié)為虛指針勤讽;虛函數(shù)表中的前面的指針為虛函數(shù)指針,最后才是指向
type_info
對象的指針
C++類成員函數(shù)
"類根本就沒有成員函數(shù)"
- 因?yàn)閺膬?nèi)存布局上看拗踢,內(nèi)存布局中只有成員變量的空間脚牍,并沒有成員函數(shù)的內(nèi)存空間
- 當(dāng)我們在一個類中聲明一個成員函數(shù)時,編譯器隱藏了第一個參數(shù)巢墅,實(shí)際上成員函數(shù)儲存在代碼區(qū)莫矗,長這個樣子:
void memberFunc(Object* this, int arg1, int arg2)
并且這個函數(shù)參數(shù)this只有接收所屬類類型指針時才能調(diào)用 - 所謂的
.
運(yùn)算符或者->
運(yùn)算符,實(shí)際是把this
指針傳遞給函數(shù)砂缩,調(diào)用時長這個樣子:
void memberFunc(this, this->arg1, this->arg2)
C++對象模型優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):相對于簡單模型的通過指針間接訪問數(shù)據(jù)的思想,C++模型提高了訪問數(shù)據(jù)的效率三娩,并參考表格驅(qū)動理念設(shè)計了虛函數(shù)表庵芭,節(jié)約了對象空間。
- 缺點(diǎn):修改對象的非靜態(tài)成員變量(增刪改)雀监,用到此對象的代碼就需要重新編譯双吆。
深入理解虛函數(shù)、虛函數(shù)表
作用:
- 為了實(shí)現(xiàn)多態(tài)的機(jī)制会前,簡而言之就是用父類指針指向其子類的實(shí)例好乐,然后通過父類的指針調(diào)用實(shí)例子類的成員函數(shù)。這種技術(shù)可以讓父類的指針有"多種形態(tài)"——多態(tài)
虛函數(shù)表
- 即類的虛函數(shù)的地址表瓦宜,表的本質(zhì):指針數(shù)組
- 表的最后一個位是
null
指針 -
只有有虛函數(shù)的類蔚万,他的內(nèi)存中才會有虛指針,且該指針存在于實(shí)例中最前面的位置(保證取到虛函數(shù)表的有最高的性能)
一般繼承(無虛函數(shù)重寫時)
- 虛函數(shù)按照其聲明順序放于表中
- 父類的虛函數(shù)在子類的虛函數(shù)前面
一般繼承(有虛函數(shù)重寫時)
- 覆蓋的函數(shù)被放到了虛表中原來父類虛函數(shù)的位置临庇,由于函數(shù)指針被子類函數(shù)取代反璃,所以實(shí)際調(diào)用時昵慌,調(diào)用的是子類的同名函數(shù),因此實(shí)現(xiàn)多態(tài)
- 沒有被覆蓋的函數(shù)依舊
多重繼承(無虛函數(shù)重寫時)
- 子類實(shí)例中淮蜈,針對每個父類都有自己的虛表斋攀,因此每多繼承一個父類,子類實(shí)例中僅僅只是增加一個虛指針的空間而已
- 子類自己的虛函數(shù)被放到了第一個父類的表中梧田,按照聲明順序確定誰是第一父類
這樣做就是為了解決不同的父類類型的指針指向同一個子類實(shí)例淳蔼,而能夠調(diào)用到實(shí)際的函數(shù)。
多重繼承(有虛函數(shù)重寫時)
- 三個父類虛函數(shù)表中的f()的位置被替換成了子類的函數(shù)指針裁眯。這樣鹉梨,我們就可以任一靜態(tài)類型的父類來指向子類,并調(diào)用子類的f()了
虛函數(shù)表的缺點(diǎn)
-
不安全性:來源于虛函數(shù)表的本質(zhì)還是一個指針數(shù)組
- 例如上圖未状,根據(jù)C++語義俯画,在沒有重寫父類的虛函數(shù)時,我們是無法通過父類指針來調(diào)用子類自己的虛函數(shù)
Base * base = new Derive;
base->g1(); // 編譯錯誤
- 但是根據(jù)繼承模型司草,子類對象還是會將父類與自己的所有虛函數(shù)放在一個虛表中,因此就導(dǎo)致可以通過指針的方式訪問虛函數(shù)表中的任意函數(shù)
- 同樣的道理艰垂,即使是
non-public
的繼承方式,這些非public的虛函數(shù)同樣會存在于虛函數(shù)表中