對靜態函式的呼叫實際上經歷了以下的過程:
1.改寫函式原型,安插乙個this指標作為額外的引數
float point3d::magnitude3d() const{};
float magituded3d(const point3d*_this){};2.將函式內對非靜態成員的訪問改為經由this指標來訪問(靜態成員不屬於特定的某個例項,屬於整個類,不應被繫結在this指標上)
3.將成員函式重新寫成乙個新的外部函式,對函式名稱進行改寫,使這個函式在程式中獨一無二
4.nrv優化:將返回值作為乙個引數,以引用的形式放置在引數列表中,返回時也直接返回這個值。
x bar()--------->void bar(this,x&_result)如果normalize()是乙個虛擬成員函式,則ptr->normalize();被轉換為:
(*ptr->vptr[1])(ptr)ptr經由虛函式指標vptr,找到對應的虛函式表,再在其中找到對應的函式,跳轉到該函式位置,傳遞乙個this指標進行呼叫。
對於非多型的情況,函式呼叫如obj.normalize()不會經由vptr去呼叫,這樣與普通的非靜態函式的效率一致。
靜態成員函式不能直接訪問非靜態成員,也不能呼叫非靜態成員函式。如果要呼叫非靜態成員函式,必須在引數中傳遞乙個該非靜態成員函式所屬的例項,通過該例項來呼叫非靜態成員函式。這是因為非靜態成員函式是不與this相繫結的,因此必須要指明所呼叫的非靜態成員函式到底屬於哪個物件。
靜態成員函式的特點在於:
1.不能直接訪問非靜態成員與呼叫非靜態成員函式。
2.不能被宣告為const,volatile或virtual。(因為不能被this指標繫結)
對於ptr->z()
需要哪些資訊才能使在執行期呼叫正確的z()實體:
1.ptr所指向的真實型別
2.z()的位置
在編譯期,虛函式位址被確定下來,這些函式和位址是不會在執行期改變的。
另外,在類中安插乙個新的指標vptr,用於指向虛函式表,通過虛函式表來呼叫虛函式。
單一繼承下:p157
這圖也太他媽糊了
多重繼承下:
多重繼承下的虛成員函式會變得複雜,主要出現在第二個及第三個或第四個...的基類身上,因為必須在執行期調整this指標
設:class a{};
class b{};
class c:public a,public b{};
當使用基類指標時:
a* a=new c;
此時與單一繼承沒有什麼區別,a指向c的起始位址。
而在第二個base class時:
b* b=new c;
此時b指向的位址必須調整,指向b subobject的起始處而不是物件的起始位址。
如果不這樣調整,通過b來呼叫b類(不是c)的成員就會出錯:b->data_b;
這是因為a和b是平級的關係,因此對c來說,它也是相當於直接繼承自b,而c++中對成員的訪問實際上是通過起始位址+類中的偏移量來實現的,如果不考慮a,b中的成員的的位址就是相對於b的偏移,而現在如果不調整b的指向,b->data_b將變為a的起始位址+b的偏移,則訪問的不知道是什麼資料成員。
同時,如果呼叫b的析構函式:delete b;
此時又要調整b的指標指向到c的起始處。
這是因為,我們要保證delete b與delete a達到相同的效果,即先進性c的析構,在進行b,在進行a的析構。
如果不調整,將會出現析構完c,b後就結束的情況(是這樣嗎???、)
而調整至c的起始位址後,保證無論基類指標是哪乙個,都以相同的析構順序進行。
因此對於a與b來說,即使他們達成相同的析構結果,但實際上:
對b的析構中首先進行了指標指向的調整,這是發生在執行期的。
這是第一種情況,即借由指向第二個及以後的指標呼叫派生類的虛函式。
第二種情況是:
通過乙個指向派生類的指標呼叫繼承自第二個及以後的基類的虛函式。
c* c=new c;
c->funcb;
這是因為c一開始是指向的c的起始位址,而各個基類的vptr的偏移是固定的,如果不調整c的指向至b subobject處,則實際上將呼叫繼承自a的vptr所指向的虛函式**中同位置slot的函式,而鬼知道這是個什麼東西。
因此此時要調整指向至b object.
第三種情況是:
b* b1=new c;
b* b2=b1->func();
說明一下func.
func函式為a,b中都有的虛函式,c中也對這個虛函式進行了改寫,該函式在a中返回乙個a*,在b中返回乙個b*,在c中返回乙個c*。
首先看b1->func,此時b1被調整至c的起始位址,然後func的c版本被呼叫,傳回乙個c*指標,在b2得到這個c*之前,必須將這個返回指標的位置調整至b subobejtct處,如果b b2=new c一般。
否則b2將指向c的起始處,而導致呼叫發生很多問題。
圖在書上p165 這個圖你tm在模糊一點
另外還有一點:
b* b=new c;
經由b只能訪問b基類的成員,而不能訪問派生類新增的甚至是a類的成員。因為b的靜態繫結知名了b是b類的,因此他只能訪問b類的成員。
但我們可以通過dynmic_cast或其他轉換方式將他轉換成c類,這樣就a,b,c的成員都可呼叫了。
深度探索C 物件模型
傳世經典書叢 深度探索c 物件模型 美 stanley b.lippman 斯坦利 b.李普曼 著 侯捷 譯 isbn978 7 121 14952 8 2012年1月出版 定價 69.00元 16開 356頁 宣傳語 如果你是一位c 程式設計師,渴望對於底層知識獲得乙個完整的了解,那麼本書正適合你...
深度探索C 物件模型
傳世經典書叢 深度探索c 物件模型 美 stanley b.lippman 斯坦利 b.李普曼 著 侯捷譯 isbn978 7 121 14952 8 2012年1月出版 定價 69.00元 16開 356頁 宣傳語 如果你是一位c 程式設計師,渴望對於底層知識獲得乙個完整的了解,那麼本書正適合你 ...
深度探索C 物件模型
深度探索c 物件模型 本書目錄結構如下 第1章 關於物件 object lessons 加上封裝後的布局成本 layout costs for adding encapsulation 1.1 c 模式模式 the c object model 簡單物件模型 a object model 驅動物件模...