?? 深度探索c++對象模型(7).txt
字號:
關于《深度探索C++對象模型》停頓了半個月,今天繼續(xù)啃這個骨頭,我的學習進入了第四章,函數(shù)的語意學。先做個復習C++支持三種成員函數(shù):靜態(tài)、虛、和非靜態(tài)。每一種函數(shù)的調(diào)用方式都不同,當然他們的作用也會有區(qū)別,一般來說我們只要掌握根據(jù)我們的需要正確的使用這三種類型的成員函數(shù)便可以了,至于內(nèi)部是如何運做的我們可以不知。但是《深度探索C++對象模型》正是讓我們對這些不知道的東西進行深度探索的一本書。通過前面的學習,我想我知道了一些以前不知道的東西,但是感覺并沒有提高多少,也許是我對此書的學習還停留在一個比較膚淺的層次上吧。我想我應該會抽時間再看幾遍。有些跑題了,因為雷神想說明一下,這些筆記只是雷神看書是的一些想法的記錄,如果你再看僅供參考,因為我本人好象也只探索了不是很深的程度。 我們的在設計和使用類時最常用的便是非靜態(tài)成員函數(shù),使用成員函數(shù)是為了封裝和隱藏我們的數(shù)據(jù),我想這是成員函數(shù)和外部函數(shù)的最明顯的區(qū)別。但是他們的效率是否有不同呢?我們不會想為了保護我們的數(shù)據(jù)而使用成員函數(shù),最后確導致效率降低的結(jié)果。讓我們看看非靜態(tài)成員函數(shù)在實際的執(zhí)行時被編譯器搞成了什么樣子。
float magnitude3d(const Point3d *_this){…}
//這是一個外部函數(shù),它有參數(shù)。表示它間接的取得坐標(Point3d)成員。
float Point3d::mangnitude3d() const {…}
//這是一個成員函數(shù),它直接取得坐標(Point3d)的成員。
表面上看,似乎成員函數(shù)的效率高很多,但實際上他們的效率真的想我們想象的那樣嗎?非也。實際上一個成員函數(shù)被內(nèi)部轉(zhuǎn)化成了外部函數(shù)。
1、 一個this指針被加入到成員函數(shù)的參數(shù)中,為的是能夠使類的對象調(diào)用這個函數(shù)。
2、 將對所有非靜態(tài)數(shù)據(jù)成員的存取操作改為由this來存取。
3、 對函數(shù)的名稱進行重新的處理,使它成為程序中獨一無二的。
這時后,經(jīng)過以上的轉(zhuǎn)換,成員函數(shù)已經(jīng)成為了非成員函數(shù)。
float Point3d::mangnitude3d() const {…}//成員函數(shù)將被變成下面的樣子
//偽碼
mangnitude3d__7Point3dFv(register Point3d * const this)
{
return sqrt(this->_x * this->x+
this->_y * this->y+
this->_z * this->z);
}
調(diào)用此函數(shù)的操作也被轉(zhuǎn)換
obj. mangnitude3d()
被轉(zhuǎn)換成:
mangnitude3d__7Point3dFv(*obj);
怎么樣看出來了吧,和我們開始聲明的非成員函數(shù)沒有區(qū)別了。因此得出結(jié)論:兩個鐵球同時落地。
一般來說,一個成員的名稱前面會被加上類的名稱,形成唯一的命名。實際上在對成員名稱做處理時,除了加上了類名,還會將參數(shù)的鏈表一并加上,這樣才能保證結(jié)果是獨一無二的。
我們在來看看靜態(tài)成員函數(shù)。我們有這樣的概念,成員函數(shù)的調(diào)用必須是用類的對象,象這樣obj.fun();或者這樣ptr->fun().但實際上,只有一個或多個靜態(tài)數(shù)據(jù)成員被成員函數(shù)存取時才需要類的對象。類的對象提供一個指針this,用來將用到的非靜態(tài)數(shù)據(jù)成員綁定到類對象對應的成員上。如果沒有用到任何一個成員數(shù)據(jù),就不需要用到this指針,也就沒有必要通過類的對象來調(diào)用一個成員函數(shù)。而且我們還知道靜態(tài)數(shù)據(jù)成員是在類之外的,可以被視做全局變量的,只不過它只在一個類的生命范圍內(nèi)可見。(參考前面的筆記)。而且一般來說我們會將靜態(tài)的數(shù)據(jù)成員聲明為一個非Public。這樣我們便必須提供一個或多個成員函數(shù)用來存取這個成員。雖然我們可以不依靠類的對象存取靜態(tài)數(shù)據(jù)成員,但是這個可以用來存取靜態(tài)成員的函數(shù)確實必須綁定在類的對象上的。為了更加好的解決這個問題,cfront2.0引入了靜態(tài)成員函數(shù)的概念。
靜態(tài)成員函數(shù)是沒有this指針的。因為它不需要通過類的對象來調(diào)用。而且它不能直接存取類中的非靜態(tài)成員。并且不能夠被聲明為virtual,const,volatile.如果取得一個靜態(tài)成員函數(shù)的地址,那么我們獲得的是這個函數(shù)在內(nèi)存中的位置。(非靜態(tài)成員函數(shù)的地址我們獲得的是一個指向這個類成員函數(shù)的指針,函數(shù)指針)。可以看到由于靜態(tài)成員函數(shù)沒有this指針,和非成員函數(shù)非常的相似。
有了前面幾章的基礎,好象這些描述理解起來也不很費勁,而且我們的思路可以跟著書上所說的一路傾瀉下來,這便是讀書的樂趣所在了,如果一本書讀起來都想讀第一章時那樣費勁,我想我讀不下去的可能性會很高。
繼續(xù)我們的學習,下面書上開始將虛函數(shù)了。我們知道虛函數(shù)是C++的一個很重要的特性,面向?qū)ο蟮亩鄳B(tài)便是由虛函數(shù)實現(xiàn)的。多態(tài)的概念是一個用一個public base class的指針(或者引用),尋址出一個派生類對象。虛函數(shù)實現(xiàn)的模型是這樣。每一個類都有一個虛函數(shù)表,它包含類中有作用的虛函數(shù)的地址,當類產(chǎn)生對象時會有一個指針,指向虛函數(shù)表。為了支持虛函數(shù)的機制,便有了“執(zhí)行期多態(tài)”的形式。
下面這樣。
我們可以定義一個基類的指針。
Point *ptr;
然后在執(zhí)行期使他尋址出我們需要的對象。可以是
ptr =new Point2d;
還可以是
ptr=new Pont3d;
ptr這個指針負責使程序在任何地方都可以采用一組由基類派生的類型。這種多態(tài)形式是消極的,因為它必須在編譯時期完成。與之對應的是一種多態(tài)的積極形式,即在執(zhí)行期完成用指針或引用查找我們的一個派生類的對象。
象下面這樣:
ptr->z();
要想達到我們目的,這個函數(shù)z()應該是虛函數(shù),并且還應該知道ptr所指的對象的真實類型,以便我們選擇z()的實體。以及z()實體的位置,以便我們能夠調(diào)用它。這些工作編譯器都會為我們做好,編譯器是如何做的呢?
我們已知每一個類會有一個虛函數(shù)表,這個表中含有對應類的對象的所有虛函數(shù)實體的地址,并且可能會改寫一個基類的虛函數(shù)實體。如果沒有改寫基類存在的虛函數(shù)實體,則會繼承基類的函數(shù)實體,這還沒完,還會有一個pure_virtual_called()的函數(shù)實體。每一個虛函數(shù)不論是繼承的還是改寫的,都會被指派一個固定的索引值,這個索引在整個繼承體系中保持與特定的虛函數(shù)關聯(lián)。
說明:當沒有改寫基類的虛函數(shù)時,該函數(shù)的實體地址是被拷貝到派生類的虛函數(shù)表中的。
這樣我們便實現(xiàn)了執(zhí)行期的積極多態(tài)。這種形式的特點是,我們從頭到尾都不知道ptr指針指向了那一個對象類型,基類?派生類1?派生類2?我們不知道,也不需要知道。我們只需要知道ptr指向的虛函數(shù)表。而且我們也不知道z()函數(shù)的實體會被調(diào)用,我們只知道z()函數(shù)的函數(shù)地址被放在虛函數(shù)表中的位置。
總結(jié):在單一繼承的體系中,虛函數(shù)機制是一種很有效率的機制。我們判斷一個類是否支持多態(tài),只需要看它有沒有虛函數(shù)便可以了。 好了今天就到這里,雷神必須加快學習這本書的速度了,好象現(xiàn)在也可以快一些了。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -