上一节,我们讲到了虚函数,那么你知道虚函数是如何做到多态的吗?
虚函数是通过后期绑定,在执行时间接通过一张虚函数表,间接调用欲绑定的函数。表中的每一个元素都指向虚函数的地址。当然,编译器也会为类增加一项成员变量,此成员变量是一个指向虚函数表的指针。可用图解表示如下:
注意:
成员函数memfuc()经过编译之后,形成代码,然后放在内存中的代码区,并不是数据段。成员函数都是放在一起,而且由同一个类的所有对象共享。,用sizeof(a)计算的结果是不包括代码段的。
以下几点,等下我们一一论证:
1.每一个由此类派生出来的对象,都有一个vptr。当我们通过对象调用虚函数时,实际上是通过vptr找到虚函数表,然后通过虚函数表找到真正的虚函数地址。
2.虚函数表中的内容是根据类中的虚函数声明顺序一个一个填 入函数指针。
3.当派生类继承了基类后,同时也继承了虚函数表,当我们在派生类中修改了虚函数时,其对应的虚函数表中所指向的函数地址,就不再是基类的了,而是派生类的了。此点我们用图解如下:
现在我们来分析一下到底是不是这样的?这一节讲解以下面的代码为例(在上一节的例子基础上修改而成)
Code
#include <iostream.h>
#include <stdio.h>
#include <string.h>
class CEmployee
{
public:
int m_name;
public:
CEmployee(){};
virtual int computerPay()
{
printf("这是基类的计算方法");
return(2);
}
virtual void display()
{
printf("职员");
}
};
class CWage:public CEmployee
{
public:
int m_wage;
int m_hour;
public:
CWage(){};
void set(int wage,int hour)
{
m_wage=wage;
m_hour=hour;
}
virtual int computerPay()
{
return m_wage*m_hour;
}
virtual void display()
{
printf("时薪职员");
}
};
class CSales:public CWage
{
public:
int n_sale;
int n_hour;
public:
CSales(){};
void set(int sale,int hour)
{
n_sale=sale;
n_hour=hour;
}
virtual int computerPay()
{
return n_sale*n_hour+CWage::computerPay();
}
virtual void display()
{
printf("销售员");
}
};
void main()
{
cout<<sizeof(CEmployee)<<endl;
cout<<sizeof(CWage)<<endl;
cout<<sizeof(CSales)<<endl;
CEmployee a;
CWage b;
CSales c;
b.m_name=1;
b.m_wage=2;
b.m_hour=3;
cout<<b.m_name<<endl;
cout<<b.m_wage<<endl;
cout<<b.m_hour<<endl;
cout<<&b<<endl;
cout<<&(b.m_name)<<endl;
cout<<&(b.m_wage)<<endl;
cout<<&(b.m_hour)<<endl;
cout<<&c<<endl;
}
运行后的结果如下:
我们来分析一下:
1.sizeof(CEmployee)的大小为8,而这个类中只有一个整型是4个字节,那么还有四个字节是谁占了呢,对了,就是指向虚函数表的vptr成员变量。说明确实在类中只要有虚函数,就一定会有vptr这个成员变量。
2.sizeof(CWage)的大小为16,实际上是基类的整型+本类的两个整型+从基类继承的vptr也占用了四个字节,刚好说明派生类继承了虚函数表。
3.看清0x0012ff68这个内存地址,它是类CWage对象的起始地址,这个地址中的内容刚好是vptr,我们跟踪程序,调试时可查,我把图取上来大家看一下:
4.虚函数表中的内容是根据类中的虚函数声明顺序一个一个填 入函数指针。大家看上图先是压入CWag::computerPay,然后再是CWage::display。
5.当派生类继承了基类后,同时也继承了虚函数表,当我们在派生类中修改了虚函数时,其对应的虚函数表中所指向的函数地址,就不再是基类的了,而是派生类的了。现在我们看到的是b对象(即CWage),转到C对象时,会发现虚函数表中的地址都发生变化了。
今天就先写到这儿,本人才shu学浅,有什么意见或见意,希望能与大家一同探讨。