#include<iostream> using namespace std;
class A{ public: int i; char c; void f(){cout<<"A::f()"<<endl;} virtual void vf(){cout<<"A::vf()"<<endl;} A():i(11){} }a;
class B{ public: void f(){cout<<"B::f()"<<endl;} virtual void vf(){cout<<"B::vf()"<<endl;} int c; int i; B():i(22),c(66){} }b;
int main(){ A *pA=(A*)&b; pA->f();/*編譯時就決定了調用A::f()。因為不是虛函數,不存在"運行時決定類型"。 (所謂運行時決定類型,其實就是在調用虛函數時,將根據對象的內存地址來偏移,找到虛函數表。。。) 類的非虛成員函數其實是全局函數(因此,就存儲空間而言,它”不屬于“類),調用時根據隱 含指針this來決定調用哪個,其實就是全局的重載函數——根據形參決定調用版本。 這里傳遞的是A類指針,當然調用的是void f(A* this) 這個版本。 因此,編譯時就確定了調用的哪個函數。 為什么不在運行時決定?因為它”不屬于“類,它是全局的,不可能根據類對象地址來偏移來尋址。 */ cout<<pA->i<<endl;/*編譯時認為將輸出A::i,但是運行時根據類對象尋址類成員變量, 輸出的其實是:int(b對象的地址 + a對象中i相對于&a的偏移)。 為什么要在運行時決定?因為它是“屬于”類的,要根據類對象的地址來偏移尋址。 cout<<pA->b<<endl;編譯時認為將輸出A::b,所以編譯出錯:"class A has no member named 'b' */ pA->vf();/*運行時決定。 他是怎么做到運行時決定的呢?魔術,說穿了,所謂“運行時動態類型”根本就是一個障眼法。 編譯時,調用什么并不知道,只知道調用的是一個虛函數,因此,根據指針指向的對象——b來尋址,(其實,此刻它仍然把pA指向的b當作A類對象,但是同一個編譯器下虛表相對于對象起始地址的偏移是編譯器已經確定了的。)找到虛函數表地址(虛表是在類內部的),然后根據虛表找到虛函數地址。 因此,“運行時決定”的立足點是虛函數。 */ cout<<"\n------------------>"<<endl; cout<<sizeof b<<" "<<sizeof a<<endl; /*VC中,類對象各成員的存儲順序依次為:虛表(如果有則)4字節,非靜態成員變量按聲明順序依次(默認4字節對齊)。 靜態成員和成員函數都不在類對象的“內部”*/ cout<<int(* ((char*)&b+4) )<<endl; cout<<int(* ((char*)&b+8) )<<endl; typedef void(*F)(); F ff= (F)*( (int*)*(int*)&b +0 ) ; ff(); cout<<"<------------------\n"<<endl;
system("pause"); }
|