关于虚表和虚指针的具体实现方法,最近看到了一个我觉得比较符合逻辑的解释:
动态绑定的实现机制(三步)
1.为每一个包含虚函数的类设置一个虚表(VTABLE)
每当创建一个包含有虚函数的类或从包含虚函数的类派生一个类时,编
译器就会为这个类创建一个VTABLE。在VTABLE中,编译器放置了这个
类中,或者它的基类中所有已经声明为 virtual 的函数的地址。如果在这
个派生类中没有对基类中声明为 virtual 的函数进行重新定义,编译器就
使用基类的这个虚函数的地址。而且所有VTABLE中虚函数地址的顺序是
完全相同的。
2.初始化虚指针(VPTR)
然后编译器在这个类的各个对象中放置VPTR。VPTR在对象的相同的位置(通常都
在对象的开头)。VPTR必须被初始化为指向相应的VTABLE。
3.为虚函数调用插入代码
当通过基类的指针调用派生类的虚函数时,编译器将在调用处插入相应
的代码,以实现通过VPTR找到VTABLE,并根据VTABLE中存储的正确
的虚函数地址,访问到正确的函数。
为了搞清楚虚指针(VPTR)的具体空间分配情况,做了如下的试验
试验一
#include
#include
using namespace std;
class cdoc
{
public:
virtual void serialize()
{cout<<"cdoc::serialize";}
void func()
{serialize();}
private:
int a;
char b[3];
};
class cmydoc:public cdoc
{
public:
virtual void serialize()
{cout<<"cmydoc::serialize";}
};
int main()
{
cout<<"Size of cdoc:"< cout<<"Size of cmydoc:"<
return 0;
}
运行的结果为:
Size of cdoc:12
Size of cmydoc:12
试验二
我们稍加改动,将
class cmydoc:public cdoc
{
public:
virtual void serialize()
{cout<<"cmydoc::serialize";}
};
改为:
class cmydoc:virtual public cdoc
{
public:
virtual void serialize()
{cout<<"cmydoc::serialize";}
};
运行结果变为:
Size of cdoc:12
Size of cmydoc:16
试验三
改为:
class cmydoc
{
public:
virtual void serialize()
{cout<<"cmydoc::serialize";}
};
运行结果变为:
Size of cdoc:12
Size of cmydoc:4
总结:
1)试验三说明:sizeof某个class代表的是以这个class创建对象时给它分配的真实空间的大小,可以看出在32位机器上面,每个包含虚函数的对象是有一个4个字节的虚指针(VPTR)空间的
2)普通继承下,试验一表明每个包含虚函数的对象只有一个指向该类虚表的指针,并不继承父类的虚指针
3)虚继承下,试验二表明实现虚继承时,会需要额外的空间的,要跟虚函数实现的多态的空间分配区分开来
阅读(720) | 评论(0) | 转发(0) |