前述
看完《深度探索c++對象模型》冯袍,心中對c++編譯器在編譯期間所做的處理有了更深入的認識,我想碾牌,除了對編譯器本身有深入認識的作者之外康愤,應(yīng)該很少有人對c++的對象模型有這么深的認識。能接觸了這本書舶吗,是我們的幸運征冷,是作者讓我們有機會能一窺其貌,感謝作者誓琼。
其實第一遍讀這本書检激,我的收獲還不算多,這可能是我對c++的使用還不夠多的緣故腹侣,但通過這本書叔收,我以后使用c++的時候,就會心里有更多的底氣傲隶,也會有更多需要注意的地方饺律,在經(jīng)過更多的實踐之后,我一定還會回來拜讀這本書的跺株。
現(xiàn)在复濒,我想就本書所學(xué)到的的知識做一些總結(jié)。
參考書籍及鏈接:
《深度探索c++對象模型》
https://www.cnblogs.com/lengender-12/p/6944042.html
一帖鸦、關(guān)于對象
1. C++在加入封裝后(只含有數(shù)據(jù)成員和普通成員函數(shù))的布局成本增加了多少芝薇?
答案是并沒有增加布局成本。就像C struct一樣作儿,memeber functions雖然含在class的聲明之內(nèi)洛二,卻不出現(xiàn)在object中。每一個non-inline member function只會誕生一個函數(shù)實體攻锰。至于每一個“擁有零個或一個定義的” inline function則會在其每一個使用者(模塊)身上產(chǎn)生一個函數(shù)實體晾嘶。
2. C++在布局以及存取時間上主要的額外負擔(dān)是由virtual引起的,包括:
virtual funciton機制娶吞,用以支持一個有效率的“執(zhí)行期綁定”
virtual base class垒迂,用以實現(xiàn)“多次出現(xiàn)在繼承體系中的base class,有一個單一而被共享的實體”
二妒蛇、C++ 對象模式(The C++ Object Model)
1. 在C++中机断,有兩種class data members:static 和 nonstatic楷拳,以及三種class member functions:static、nonstatic和virtual吏奸。
2. C++對象模型中欢揖,nonstatic data members被配置于每一個class object之內(nèi)。
static data members則被存放在所有的class object之外奋蔚。static和nonstatic function members也被放在所有的class object之外她混。virtual function則以兩個步驟支持之:
- 每個class產(chǎn)生出一堆指向virtual functions的指針,放在表格之中泊碑。這個表格被稱為virtual table(vtbl)
- 每一個class object被安插一個指針坤按,指向相關(guān)的virtual table。通常這個指針被稱為vptr馒过。vptr的設(shè)定和重置都由每一個class的constructor臭脓、destructor和copy assignment運算符自動完成。每一個class所關(guān)聯(lián)的type_info object(用以支持runtime type identification, RTTI)也經(jīng)由virtual table被指出來沉桌,通常放在表格的第一個slot處膳殷。
這個模型的主要優(yōu)點在于它的空間和存取時間的效率鸠信。
主要缺點是:如果應(yīng)用程序代碼未曾改變,但所用到的class objects的nonstatic data members有所修改(有可能是增加、移除或更改)呆盖,那么應(yīng)用程序代碼同樣得重新編譯血淌。
3.繼承關(guān)系可以指定為虛擬(virtual判呕,也就是共享的意思):
在虛擬繼承的情況下品嚣,base class不管在繼承鏈中被派生(derived)多少次,永遠只會存在一個實例(稱為subobject)求冷。
三瘤运、關(guān)鍵詞帶來的差異
1.什么時候一個人應(yīng)該在c++程序中以struct取代class?
答案之一是當(dāng)他讓人感覺比較好的時候。單獨來看匠题,關(guān)鍵詞本身并不提供任何差異拯坟,c++編譯器對二者都提供了相同支持,我們可以認為支持struct只是為了方便將c程序遷移到c++中韭山。
88
2.那為什么我們要引入class關(guān)鍵詞郁季?
這是因為引入的不只是class這個關(guān)鍵詞,更多的是它所支持的封裝和繼承的哲學(xué)钱磅。
3.怎么在c++中用好struct梦裂?
將struct和class組合起來,組合盖淡,而非繼承年柠,才是把c和c++結(jié)合在一起的唯一可行的方法。另外褪迟,當(dāng)你要傳遞“一個復(fù)雜的class object的全部或部分”到某個c函數(shù)去時冗恨,struct聲明可以將數(shù)據(jù)封裝起來答憔,并保證擁有與c兼容的空間布局。
四掀抹、對象的差異
1. C++程序設(shè)計模型直接支持三種程序設(shè)計典范(programming paradigms):
- 程序模型:數(shù)據(jù)和函數(shù)分開攀唯。
- 抽象數(shù)據(jù)類型模型:數(shù)據(jù)和函數(shù)一起封裝以來提供。
- 面向?qū)ο竽P停嚎赏ㄟ^一個抽象的base class封裝起來渴丸,用以提供共同接口,需要付出的就是額外的間接性另凌。
雖然你可以直接或間接處理繼承體系中的一個base class object,但只有通過pointer或reference的間接處理谱轨,才支持OO程序設(shè)計所需的多態(tài)性質(zhì)。c++通過class的pointers和reference來支持多態(tài)吠谢,這種程序設(shè)計風(fēng)格就稱為面向?qū)ο?/strong>
Liberary_materials thing1;//基類
Book book;//派生類
thing1=book;
thing1.check_in();//這種情況下土童,調(diào)用的是基類的check_in()
Liberary_materials &thing2=book
thing2.check_in();//這種情況下調(diào)用的才是book的check_in()
2. C++以下列方法支持多態(tài):
- 經(jīng)由一組隱式的轉(zhuǎn)化操作。例如把一個derived class指針轉(zhuǎn)化為一個指向其public base type的指針
shape *ps=new circle();
- 經(jīng)由virtual function機制
ps->rotate();
- 經(jīng)由dynamic_cast和typeid運算符
if(circle *pc=dynamic_cast<circle *>(ps))...
多態(tài)的主要用途是經(jīng)由一個共同的接口來影響類型的封裝工坊,這個接口通常被定義在一個抽象的base class中献汗。這個共享接口是以virtual function機制引發(fā)的,它可以在執(zhí)行期根據(jù)object的真正類型解析出到底是哪一個函數(shù)實體被調(diào)用王污。
3. 需要多少內(nèi)存才能表現(xiàn)一個class object?
- 其nonstatic data members的總和大小
- 加上任何由于aliginment的需求而填補上去的空間(可能存在于members之間罢吃,也可能存在于集合體邊界),aliginement就是將數(shù)值調(diào)整到某數(shù)的倍數(shù),如在32位的計算機上為4昭齐。
- 加上為了支持virtual而由內(nèi)部產(chǎn)生的任何額外負擔(dān)
4. 一個指針(引用)尿招,不管它指向哪一種數(shù)據(jù)結(jié)構(gòu),指針本身所需的內(nèi)存大小是固定的(一個機器字)阱驾。
例如:一個指向ZooAnimal的指針是如何地與一個指向整數(shù)得指針或一個指向template Array的指針有所不同的呢就谜?
ZooAnimal *px;
int *pi;
Array<string> *pta;
以內(nèi)存需求的觀點來說,沒有什么不同里覆!它們?nèi)齻€都需要足夠的內(nèi)存來放置一個機器地址(通常是個word)丧荐。“指向不同類型的各指針”間的差異,既不在其指針表示法不同喧枷,也不在其內(nèi)容(代表一個地址)不同虹统,而是在其所尋址出來的object類型不同,也就是說割去,“指針類型”會教導(dǎo)編譯器如何解釋某個特定地址中的內(nèi)存內(nèi)容及其大小窟却。
5.轉(zhuǎn)型(cast)其實是一種編譯器指令。
大部分情況下它并不改變一個指針?biāo)恼嬲刂飞肽妫挥绊憽氨恢赋鲋畠?nèi)存大大小和其內(nèi)容”的解釋方式夸赫。
如一個類型為void *的指針只能夠持有一個地址,但不能 通過它操作所指object咖城。
6.一個基類指針和其派生類指針有什么不同茬腿?(單一一層繼承呼奢,且其都指向派生類對象)
二者都指向基類對象的第一個byte,其間的差別是,派生類指針涵蓋的地址包含整個派生類對象切平,而一個基類指針?biāo)w的地址只包含派生類對象的基類子對象部分握础。
但基類指針可以通過virtual機制訪問派生類對象的函數(shù)。
7.當(dāng)一個base class object被直接初始化為(或被指定為)一個derived class object時悴品。
derived object就會被切割(sliced)以塞入較小的base type內(nèi)存中禀综,derived type將沒有留下任何蛛絲馬跡。多態(tài)于是不再呈現(xiàn)苔严,而一個嚴(yán)格的編譯器可以在編譯器解析一個“通過此object而觸發(fā)的virtual function調(diào)用操作”定枷,因而回避virtual機制。如果virtual function被定義為inline届氢,則更有效率上的大收獲欠窒。
8.C++也支持具體的ADT程序風(fēng)格,如今被稱為object-based(OB)退子。
一個OB設(shè)計可能比一個對等的OO設(shè)計速度更快而且空間更緊湊岖妄。速度快是因為所有的函數(shù)調(diào)用操作都在編譯時期解析完成,對象構(gòu)建起來時不需要設(shè)置virtual機制寂祥〖雠埃空間緊湊是因為每一個class object不需要負擔(dān)傳統(tǒng)上為了支持virtual機制兒需要的額外負荷。不過丸凭,OB設(shè)計比較沒有彈性缚俏。
在彈性(OO)和(OB)之間常常存在著取舍。一個人能夠有效選擇其一之前贮乳,必須先清楚了解兩者的行為和應(yīng)用領(lǐng)域的需求忧换。