#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"); }
|