为了更加清楚的说明内存分布:下面详细的介绍内存的分布
1 基类的内存分布情况
请看下面的sample
class A
{
void g(){.....}
};
则sizeof(A)=1;
如果改为如下:
class A
{
public:
virtual void f()
{
......
}
void g(){.....}
}
则sizeof(A)=4! 这是因为在类A中存在virtual function,为了实现多态,每个含有virtual function的类中都隐式包含着一个静态虚指针vfptr指向该类的静态虚表vtable, vtable中的表项指向类中的每个virtual function的入口地址
例如 我们declare 一个A类型的object :
A c;
A d;
则编译后其内存分布如下:
从 vfptr所指向的vtable可以看出,每个virtual function都占有一个entry,例如本例中的f函数。而g函数因为不是virtual类型,故不在vtable的表项之内。说明:vtab属于类成员静态pointer,而vfptr属于对象pointer
2 继承类的内存分布状况
假设代码如下:
public B:public A
{
public :
int f() //override virtual function
{
return 3;
}
};
则
A c;
A d;
B e;
编译后,其内存分布如下:
从中我们可以看出,B类型的对象e有一个vfptr指向vtable address:0x00400030 ,而A类型的对象c和d共同指向类的vtable address:0x00400050a
3 动态绑定过程的实现
我们说多态是在程序进行动态绑定得以实现的,而不是编译时就确定对象的调用方法的静态绑定。
其过程如下:
程序运行到动态绑定时,通过基类的指针所指向的对象类型,通过vfptr找到其所指向的vtable,然后调用其相应的方法,即可实现多态。
例如:
A c;
B e;
A *pc=&e; //设置breakpoint,运行到此处
pc=&c;
此时内存中各指针状况如下:
可以看出,此时pc指向类B的虚表地址,从而调用对象e的方法。
继续运行,当运行至pc=&c时候,此时pc的vptr值为0x00420050,即指向类A的vtable地址,从而调用c的方法。
这就是动态绑定!(dynamic binding)或者叫做迟后联编(lazy compile)。