大家都知道C++动态绑定的实现是通过虚函数体现的,再深入研究一下,程序是如何知道该调用哪个函数呢?这就涉及到了虚函数的底层实现问题,类时通过虚函数表来完成虚函数寻址的。
具体来说就是,当我们用父类指针来操作一个子类时,这张虚函数表就闪亮登场了,它就像一张地图一样,指示这实际应该调用的函数。下面我们就来探索一下这张表到底有多神奇。
I、无继承实现
C++的标准规格说明书中说到,编译器必须要保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确的取得要调用虚函数的偏移量)。
根据上面引用的说法,我们有如下测试代码1:
#include<iostream>
using namespace std;
class Base {
public:
virtual void f() {cout << "Base::f" << endl;}
virtual void g() {cout << "Base::g" << endl;}
virtual void h() {cout << "Base::h" << endl;}
};
int main()
{
typedef void(*Fun)(void);
Base b;
Fun pFun = nullptr;
cout << "虚函数表地址:" << (int*)(&b) << endl;
cout << "虚函数表--第一个函数地址:" << (int*)*(int*)(&b) << endl;
pFun = (Fun)*((int*)*(int*)(&b)); //Base::f
cout << "第一个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&b) + 1); //Base::g
cout << "第二个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&b) + 2); //Base::h
cout << "第三个虚函数为:";
pFun();
cout << endl;
return 0;
}
从测试结果中可以得到下面的虚函数寻址方式示意图:
II、一般继承(无虚函数继承)
假如有如下继承关系:
注意到,这个继承关系中,子类没有重写任何父类的函数。那么在派生实例中虚函数表该是什么样子的,接下来进行如下测试2:
#include<iostream>
using namespace std;
class Base {
public:
virtual void f() {cout << "Base::f" << endl;}
virtual void g() {cout << "Base::g" << endl;}
virtual void h() {cout << "Base::h" << endl;}
};
class Derive : public Base {
public:
virtual void f1() {cout << "Derive::f1" << endl;}
virtual void g2() {cout << "Derive::g1" << endl;}
virtual void h1() {cout << "Derive::h1" << endl;}
};
typedef void(*Fun)(void);
int main()
{
Base b;
Derive d;
Fun pFun = nullptr;
cout << "父类虚函数表地址:" << (int*)(&b) << endl;
cout << "父类虚函数表--第一个函数地址:" << (int*)*(int*)(&b) << endl;
cout << "子类虚函数表地址:" << (int*)(&d) << endl;
cout << "子类虚函数表--第一个函数地址:" << (int*)*(int*)(&d) << endl;
pFun = (Fun)*((int*)*(int*)(&d)+0);
cout << "第一个虚函数为:"; //Base::f
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+1); //Base::g
cout << "第二个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+2); //Base::h
cout << "第三个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+3); //Derive::f1
cout << "第四个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+4); //Derive::g1
cout << "第五个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+5); //Derive::h1
cout << "第六个虚函数为:";
pFun();
cout << endl;
return 0;
}
测试2结果:
从测试2输出结果中可以得到如下结论:
在无虚函数重写的情况下所实现的虚函数表如下:
III、一般继承(有虚函数复写)
假设继承关系如下:
我们用下面的测试3代码观测虚函数表:
#include<iostream>
using namespace std;
class Base {
public:
virtual void f() {cout << "Base::f" << endl;}
virtual void g() {cout << "Base::g" << endl;}
virtual void h() {cout << "Base::h" << endl;}
};
class Derive : public Base {
public:
void f() override {cout << "Derive::f" << endl;}
virtual void g2() {cout << "Derive::g1" << endl;}
virtual void h1() {cout << "Derive::h1" << endl;}
};
typedef void(*Fun)(void);
int main()
{
Base b;
Derive d;
Fun pFun = nullptr;
cout << "父类虚函数表地址:" << (int*)(&b) << endl;
cout << "父类虚函数表--第一个函数地址:" << (int*)*(int*)(&b) << endl;
cout << "子类虚函数表地址:" << (int*)(&d) << endl;
cout << "子类虚函数表--第一个函数地址:" << (int*)*(int*)(&d) << endl;
pFun = (Fun)*((int*)*(int*)(&d)+0); //Derive::f
cout << "第一个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+1); //Base::g
cout << "第二个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+2); //Base::h
cout << "第三个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+3); //Derive::g1
cout << "第四个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+4); //Derive::h1
cout << "第五个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)(&d)+5); //无函数,会出现一个error
cout << "第六个虚函数为:";
pFun();
cout << endl;
return 0;
}
测试结果如下:
由测试结果可以得出在有虚函数重写的子类中虚函数表如下图所示:
IV、多重继承(无虚函数重写)
多重继承会为一个子类对象产生多个虚函数表,这也是能通过我们上面的结论得出的,因为根据测试2,我们发现父类对象的第一个虚函数与子类对象的第一个虚函数地址相同,所以如果子类继承自多个父类,则分别继承自多个父类的虚函数表应该各自有不同的起始地址,再根据偏移量寻址得到其他虚函数地址。
假设继承关系如下:
我们用下面的测试4代码观测虚函数表:
#include<iostream>
using namespace std;
class Base1 {
public:
virtual void f() {cout << "Base1::f" << endl;}
virtual void g() {cout << "Base1::g" << endl;}
virtual void h() {cout << "Base1::h" << endl;}
};
class Base2 {
public:
virtual void f() {cout << "Base2::f" << endl;}
virtual void g() {cout << "Base2::g" << endl;}
virtual void h() {cout << "Base2::h" << endl;}
};
class Base3 {
public:
virtual void f() {cout << "Base3::f" << endl;}
virtual void g() {cout << "Base3::g" << endl;}
virtual void h() {cout << "Base3::h" << endl;}
};
class Derive : public Base1, public Base2, public Base3 {
public:
virtual void f1() {cout << "Derive::f1" << endl;}
virtual void g1() {cout << "Derive::g1" << endl;}
};
typedef void(*Fun)(void);
int main()
{
Base1 b1;
Base2 b2;
Base3 b3;
Derive d;
Fun pFun = nullptr;
cout << "Base1虚函数表地址:" << (int*)(&b1) << endl;
cout << "Base1虚函数表--第一个函数地址:" << (int*)*(int*)(&b1) << endl;
cout << "Base2虚函数表地址:" << (int*)(&b2) << endl;
cout << "Base2虚函数表--第一个函数地址:" << (int*)*(int*)(&b2) << endl;
cout << "Base3虚函数表地址:" << (int*)(&b3) << endl;
cout << "Base3虚函数表--第一个函数地址:" << (int*)*(int*)(&b3) << endl;
cout << "子类虚函数表地址:" << (int*)(&d) << endl;
cout << "子类虚函数表--第一个函数地址:" << (int*)*(int*)(&d) << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); //Base1::f
cout << "第一个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); //Base1::g
cout << "第二个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); //Base1::h
cout << "第三个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); //Derive::f1
cout << "第四个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+4); //Derive::g1
cout << "第五个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); //Base2::f
cout << "第六个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); //Base2::g
cout << "第七个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+2); //Base2::h
cout << "第八个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+0); //Base3::f
cout << "第九个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+1); //Base3::g
cout << "第十个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+2); //Base3::h
cout << "第十一个虚函数为:";
pFun();
cout << endl;
return 0;
}
测试结果如下:
从输出结果中我们可以得到如下的虚函数表示意图:
V、多重继承(有虚函数重写)
假设继承关系如下:
我们用下面的测试5代码观测虚函数表:
#include<iostream>
using namespace std;
class Base1 {
public:
virtual void f() {cout << "Base1::f" << endl;}
virtual void g() {cout << "Base1::g" << endl;}
virtual void h() {cout << "Base1::h" << endl;}
};
class Base2 {
public:
virtual void f() {cout << "Base2::f" << endl;}
virtual void g() {cout << "Base2::g" << endl;}
virtual void h() {cout << "Base2::h" << endl;}
};
class Base3 {
public:
virtual void f() {cout << "Base3::f" << endl;}
virtual void g() {cout << "Base3::g" << endl;}
virtual void h() {cout << "Base3::h" << endl;}
};
class Derive : public Base1, public Base2, public Base3 {
public:
void f() override {cout << "Derive::f" << endl;}
virtual void g1() {cout << "Derive::g1" << endl;}
};
typedef void(*Fun)(void);
int main()
{
Base1 b1;
Base2 b2;
Base3 b3;
Derive d;
Fun pFun = nullptr;
cout << "Base1虚函数表地址:" << (int*)(&b1) << endl;
cout << "Base1虚函数表--第一个函数地址:" << (int*)*(int*)(&b1) << endl;
cout << "Base2虚函数表地址:" << (int*)(&b2) << endl;
cout << "Base2虚函数表--第一个函数地址:" << (int*)*(int*)(&b2) << endl;
cout << "Base3虚函数表地址:" << (int*)(&b3) << endl;
cout << "Base3虚函数表--第一个函数地址:" << (int*)*(int*)(&b3) << endl;
cout << "子类虚函数表地址:" << (int*)(&d) << endl;
cout << "子类虚函数表--第一个函数地址:" << (int*)*(int*)(&d) << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); //Derive::f
cout << "第一个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); //Base1::g
cout << "第二个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); //Base1::h
cout << "第三个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); //Derive::g1
cout << "第四个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); //Derive::f
cout << "第五个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); //Base2::g
cout << "第六个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+1)+2); //Base2::h
cout << "第七个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+0); //Derive::f
cout << "第八个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+1); //Base3::g
cout << "第九个虚函数为:";
pFun();
cout << endl;
pFun = (Fun)*((int*)*(int*)((int*)&d+2)+2); //Base3::h
cout << "第十个虚函数为:";
pFun();
cout << endl;
return 0;
}
测试结果如下:
从输出结果中我们可以得到如下的虚函数表示意图: