class Person { virtual void Display () = 0; // 纯虚函数 protected : string _name ; // 姓名 }; class Student : public Person {};
概念终于结束了,现在我们来终于要开始今天真正的主角了,虚函数表!!
何为虚函数表,我们写一个程序,调一个监视窗口就知道了。
下面是一个有虚函数的类:
#include<iostream> #include<windows.h> using namespacestd; class Base { public: virtual void func1() {} virtual void func2() {} private: inta; }; void Test1() { Base b1; } int main() { Test1(); system("pause"); return0; }
我们现在点开b1的监视窗口
这里面有一个_vfptr,而这个_vfptr指向的东西就是我们的主角,虚函数表。一会大家就知道了,无论是单继承还是多继承甚至于我们的菱形继承虚函
数表都会有不同的形态,虚函数表是一个很有趣的东西。
仔细看下面代码:
#include<iostream> #include<windows.h> using namespace std; class Base { public: virtual void func1() { cout<< "Base::func1"<< endl; } virtual void func2() { cout<< "Base::func2"<< endl; } private: inta; }; class Derive:public Base { public: virtual void func1() { cout<< "Derive::func1"<< endl; } virtual void func3() { cout<< "Derive::func3"<< endl; } virtual void func4() { cout<< "Derive::func4"<< endl; } private: int b; };
对于Derive类来说,我们觉得它的虚表里会有什么?
首先子类的fun1()重写了父类的fun1(),虚表里存的是子类的fun1(),接下来父类的fun2(),子类的fun3(),fun4()都是虚函数,所以虚表里会有4个元
素,分别为子类的fun1(),父类fun2(),子类fun3(),子类fun4()。然后我们调出监视窗口看我们想的到底对不对呢?
我预计应该是看到fun1(),fun2(),fun3(),fun4()的虚函数表,但是呢这里监视窗口只有两个fun1(),fun2(),难道我们错了????
这里并不是这样的,只有自己靠得住,我觉得这里的编译器有问题,那我们就得自己探索一下了。 但是在探索之前我们必须来实现一个可以打印虚函
数表的函数。
typedef void(*FUNC) (); void PrintVTable(int* VTable) { cout<< " 虚表地址"<<VTable<< endl; for(inti = 0;VTable[i] != 0; ++i) { printf(" 第%d个虚函数地址 :0X%x,->", i,VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout<< endl; } int main() { Derive d1; PrintVTable((int*)(*(int*)(&d1))); system("pause"); return0; }
下图来说一下他的缘由:
我们来使用这个函数:
//单继承 class Base { public: virtual void func1() { cout << "Base::func1" << endl; } virtual void func2() { cout << "Base::func2" << endl; } private: int a; }; class Derive :public Base { public: virtual void func1() { cout << "Derive::func1" << endl; } virtual void func3() { cout << "Derive::func3" << endl; } virtual void func4() { cout << "Derive::func4" << endl; } private: int b; }; typedef void(*FUNC) (); void PrintVTable(int* VTable) { cout << " 虚表地址>" << VTable << endl; for (int i = 0; VTable[i] != 0; ++i) { printf(" 第%d个虚函数地址 :0X%x,->", i, VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout << endl; } int main() { Derive d1; PrintVTable((int*)(*(int*)(&d1))); system("pause"); return 0; }
这里我就要讲讲这个传参了,注意这里的传参不好理解,应当细细的"品味".
PrintVTable((int*)(*(int*)(&d1)));
首先我们肯定要拿到d1的首地址,把它强转成int*,让他读取到前4个字节的内容(也就是指向虚表的地址),再然后对那个地址解引用,我们已经拿
到虚表的首地址了,但是此时这个变量的类型解引用后是int,不能够传入函数,所以我们再对他进行一个int*的强制类型转换,这样我们就传入参数
了,开始函数执行了,我们一切都是在可控的情况下使用强转,使用强转你必须要特别清楚的知道内存的分布结构。最后我们来看看输出结果:
到底打印的对不对呢? 我们验证一下:
这里我们通过&d1的首地址找到虚表的地址,然后访问地址查看虚表的内容,验证我们自己写的这个函数是正确的。(这里VS还有一个bug,当你第一次
打印虚表时程序可能会崩溃,不要担心你重新生成解决方案,再运行一次就可以了。因为当你第一次打印是你虚表最后一个地方可能没有放0,所以你
就有可能停不下来然后崩溃。)我们可以看到d1的虚表并不是监视器里面打印的那个样子的,所以有时候VS也会有bug,不要太相信别人,还是自己靠
得住。哈哈哈,臭美一下~
class Base1 { public: virtual void func1() { cout << "Base1::func1" << endl; } virtual void func2() { cout << "Base1::func2" << endl; } private: int b1; }; class Base2 { public: virtual void func1() { cout << "Base2::func1" << endl; } virtual void func2() { cout << "Base2::func2" << endl; } private: int b2; }; class Derive : public Base1, public Base2 { public: virtual void func1() { cout << "Derive::func1" << endl; } virtual void func3() { cout << "Derive::func3" << endl; } private: int d1; }; typedef void(*FUNC) (); void PrintVTable(int* VTable) { cout << " 虚表地址>" << VTable << endl; for (int i = 0; VTable[i] != 0; ++i) { printf(" 第%d个虚函数地址 :0X%x,->", i, VTable[i]); FUNC f = (FUNC)VTable[i]; f(); } cout << endl; } void Test1() { Derive d1; int* VTable = (int*)(*(int*)&d1); PrintVTable(VTable); // Base2虚函数表在对象Base1后面 VTable = (int *)(*((int*)&d1 + sizeof (Base1) / 4)); PrintVTable(VTable); } int main() { Test1(); system("pause"); return 0; }
//多态 析构函数 class Base { public: virtual void func1() { cout << "Base::func1" << endl; } virtual void func2() { cout << "Base::func2" << endl; } virtual ~Base() { cout << "~Base" << endl; } private: int a; }; class Derive :public Base { public: virtual void func1() { cout << "Derive::func1" << endl; } virtual ~Derive() { cout << "~Derive"<< endl; } private: int b; }; void Test1() { Base* q = new Derive; delete q; } int main() { Test1(); system("pause"); return 0; }