一、多态的构成原理
与虚表(虚函数表)有关,只要有虚函数就有虚表,虚表内放了虚函数的地址。 虚函数表: 虚函数表是通过一块连续的内存来存储虚函数的地址。这张表解决了继承,虚函数(重写)的问题。在有虚函数的对象实例中都存在一张虚函数表,虚函数表就像一张地图,指明了实际应该调用的虚函数。下面我们调出监视窗口来看一个示例中的虚表:
静态(编译时)联编: 不构成多态,编译时以确定调用函数的地址。 动态(运行时)联编:
构成多态,运行时在虚表里确定应调用函数的地址
二、多态的各种对象模型 首先写一个打印虚表的函数 typedef void(*V_FUNC) (); void PrintfVtable(int Vtable) { int *Vf_array = (int*)Vtable; printf("vtable:0x%p\n", Vtable); for (size_t i = 0; Vf_array[i] != 0; ++i) { printf("Vtable[%d]:0x%p ->", i, Vf_array[i]); V_FUNC f = (V_FUNC)Vf_array[i]; f(); } printf("---------------------------------\n"); } 改进版本(32位、64位都可以运行) typedef void(*V_FUNC) (); void PrintfVtable(int* Vtable)//传指针 { printf("vtable:0x%p\n", Vtable); int ** PPVtable = (int**)Vtable; for (size_t i = 0; PPVtable[i] != 0; ++i) { printf("Vtable[%d]:0x%p ->", i, PPVtable[i]); V_FUNC f = (V_FUNC)PPVtable[i]; f(); } } 1、多继承(有虚函数) 注意:多继承是子类虚表的内容放在第一个父类的虚表里 示例 typedef void(*V_FUNC) (); void PrintfVtable(int* Vtable)//传指针 { printf("vtable:0x%p\n", Vtable); int ** PPVtable = (int**)Vtable; for (size_t i = 0; PPVtable[i] != 0; ++i) { printf("Vtable[%d]:0x%p ->", i, PPVtable[i]); V_FUNC f = (V_FUNC)PPVtable[i]; f(); } } 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; }; void test() { Derive d; //PrintfVtable(*(int**)&d);//Base1的虚表 PrintfVtable(*((int**)((char*)&d+sizeof(Base1))));//Base2的虚表 } int main() { test(); return 0; } 下面分析d的对象模型 d继承了Base1和Base2的虚表,并且分别对Base1和Base2中的func1重写,自己的虚函数func3放在第一个父类base1 的虚表中。 下面运行打印虚表函数来验证: 和预想完全相同! 2、菱形继承(有虚函数)(复杂的多继承) class A { public : virtual void func1() { cout << "A::func1()" << endl; } virtual void func2() { cout << "A::func2()" << endl; } int _a; }; class B :public A { public: virtual void func1() { cout << "B::func1()" << endl; } virtual void func3() { cout << "B::func3()" << endl; } int _b; }; class C :public A { public: virtual void func1() { cout << "C::func1()" << endl; } virtual void func4() { cout << "C::func4()" << endl; } int _c; }; class D :public B, public C { public: virtual void func1() { cout << "D::func1()" << endl; } virtual void func5() { cout << "D::func5()" << endl; } int _d; }; void test() { D d; d.B::_a = 1; d._b = 2; d.C::_a = 3; d._c = 4; d._d = 5; PrintfVtable(*(int**)&d);//打印B的虚表 PrintfVtable(*((int**)((char*)&d+sizeof(B))));//打印C的虚表 } int main() { test(); return 0; }
下面我们来分析
打开内存窗口看对象d的地址 发现有两个指针,这两个指针分别是D从B继承的虚表指针(00 2d dd 2c),和D从C继承的虚表指针(00 2d dd 40)。 下面我们来看分析这两个虚表指针指向的内容 运行打印虚表函数后验证 和预想相同! ·菱形虚拟继承 代码与菱形继承基本相似: 在B和C继承A时前加virtual关键字 class B :virtual public A {} class C : virtual public A {}下面分析对象模型
可见只有一个_a了解决了数据冗余的问题,除了三个虚表指针外,多了两个虚基表指针(01 06 dd f4 和 01 06 db b0)。 接下来看这两个虚基表指针的内容: 可见,B的虚基表指针存了个十进制的24,发现当B的虚基表指针本身的地址(0x0101FC18)+ 24字节= &A(0x0101FC30);C的虚基表指针存了个十进制的12,发现当C的虚基表指针本身的地址(0x0101FC24) + 12字节 = &A(0x0101FC30); 再来研究这三个虚表指针的内容: 先考虑为什么有3个虚表指针,为什么不像菱形继承一样有2个虚表指针?
本来B只继承A,B的虚表内容放在A的虚表内就可以,但是现在是菱形虚拟继承,B和C只有一个公共的A,若B的虚表内容放在A的虚表内,同理C也继承A,所以C的虚表内容也应放在A的虚表里,这样一来B和C的虚表内容都在A内,B和C互相可见,这样是不对的,B和C应该是相互独立的。所以现在有3个虚表指针,B和C的虚表指针分别放各自独有的虚表内容,公共的内容(从A继承的虚表)放在A的虚表里。
下面来验证:
与预想相同!