虚函数表的一些用法
引言:虚函数对于学习C++的同行来说一点都不陌生,是实现动多态(何为静多态)的基本手段,那么编译器又是如何通过虚函数来完成所需函数的调用呢?接下来按照几个例子进行一下实验。
1、开胃菜,小例子,大视野
class Base
{
public:
virtual void Print(){cout<<"This is Base"<<endl;}
int n;
//Base(int a):n(a){};
};
class Derived: public Base
{
public:
void Print(){cout<<"This is Derived"<<endl;}
//void Print2(){cout<<"This is Derived2"<<endl;}
//Derived(int a):Base(a){};
};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
Base *pb = new Derived();
//pb->Print();
//cout<<&(pb->n)<<endl<<pb;
//pb->Out(); //无法被调用
int* pA = (int*)(*((int*)pb ));
Func pFunc = (Func)*pA;
pFunc();
return 0;
}
2、C++是如何通过虚函数机制实现多态的
上述函数输出为 “This is Derived”,说明Derived类的Print函数被调用了。那么他是如何被调用的呢,首先要引入“虚函数表”的概念,如果一个类拥有virtural函数,那么C++会位每一个该类的实例分配一个虚函数表指针,该指针指向该类的虚函数表结构。用一张图来分析下
如上为父类Base的虚函数表结构图,(例图仅供参考了),那么子类的虚函数表又是什么样子的呢
我们会发现Base::f()被Derive::f()覆盖了,这是因为子类对父类的f()函数进行类override,所以子类的函数覆盖了父类的函数,按照上面的逻辑,那么当定义个父类的指针指向一个子类的对象,那么指针就会通过虚函数表读取需要调用的函数,因为虚函数表中只存在子类函数所以调用的是子类函数(当子类override父类的相同函数时)。通过如上机制,多态被实现出来了,有点意思浩。
2、虚函数指针的位置
上面的例子因为类中只有一个虚函数,没有其他成员变量,所以类占用的空间就是虚函数指针的空间(4个字节32位)。但是当存在成员变量是是如何分布的呢?
class Base
{
public:
virtual void Print(){cout<<"This is Base"<<endl;}
int n;
Base(int a):n(a){};
};
class Derived: public Base
{
public:
void Print(){cout<<"This is Derived"<<endl;}
//void Print2(){cout<<"This is Derived2"<<endl;}
Derived(int a):Base(a){};
};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
Base *pb = new Derived(3);
//pb->Print();
cout<<&(pb->n)<<endl<<pb;
//pb->Out(); //无法被调用
int* pA = (int*)(*((int*)pb ));
Func pFunc = (Func)*pA;
//pFunc();
return 0;
}
输出:
由上面输出可看出n的地址与指针pb指向实例地址偏移了4个字节,这4个自己是什么呢由开始那个例子可以看出占用的这个4个字节正是“虚函数指针”占用的地址。
3、多重继承时的情况
上面讲了有成员变量时候内存分布,但是当多重继承时,内存又是如何分布的呢
<pre name="code" class="cpp">class Base2
{
public:
virtual void Print2(){cout<<"This is Base2"<<endl;};
};
class Base
{
public:
virtual void Print(){cout<<"This is Base"<<endl;}
int n;
Base(int a):n(a){};
};
class Derived: public Base, public Base2
{
public:
void Print(){cout<<"This is Derived"<<endl;}
//void Print2(){cout<<"This is Derived2"<<endl;}
Derived(int a):Base(a){};
};
typedef void (*Func)();
int _tmain(int argc, _TCHAR* argv[])
{
Base2 *pb = new Derived(3);
//pb->Print();
//cout<<&(pb->n)<<endl<<pb;
//pb->Out(); //无法被调用
int* pA = (int*)(*((int*)pb));
Func pFunc = (Func)*pA;
pFunc();
int* pB = (int*)(*((int*)pb - 2));
pFunc = (Func)*pB;
pFunc();
return 0;
}
输出:
This is Base2
This is Derived