参考文章:C++虚函数表剖析 参考文章:C++虚函数表分析 参考文章:C++中基类的析构函数为什么要用virtual虚析构函数
C++ 中的多态是用虚函数实现的:子类覆盖(重写)父类的虚函数,然后声明一个指向子类对象的父类指针,如 Base* base = new Derived(); 当调用base->func() ,调用的是子类的Derived::func() 。
1 2 3 4 5 6 7 8 9 10 11 class Base { public : virtual void func () ; }; class Derived : public Base{ public : void func () ; };
这种机制使用了一种动态绑定 的技术,技术核心是虚函数表 (简称虚表)。
虚函数表 每个包含了虚函数的类都有一个虚函数表(简称虚表) 当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可以调用这些虚函数,换句话说,就是一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。
1 2 3 4 5 6 7 8 9 class Base { public : virtual void vFunc1 () ; virtual void vFunc2 () ; void Func1 () ; void Func2 () ; }
类Base包含虚函数 vFunc1()、vFunc2() ,所以类Base拥有一张虚表。
类Base的虚表如下图:
虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。 普通函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。 虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就构造出来了。
虚表指针 虚表是属于类的,而不是属于某个具体的对象,同一个类的所有对象共享虚函数表; 为了指定对象的虚表,每个对象内部包含了一个虚表指针,来指向类的虚表; 为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针*__vptr,用来指向虚表,所以当类的对象被创建时便拥有了这个指针,且这个指针的值会被自动设置为指向类的虚表。
验证 (环境 Ubuntu 14.04 x86_64): - [X] 对象共享虚表
基础知识: 32位的操作系统指针长度为4个字节,64位操作系统指针长度为8个字节; 创建一个对象时,只为类中成员变量分配空间,对象之间共享成员函数。
_vptr: 使用sizeof(Base)查看上述Base类的大小时,发现sizeof(Base)=8,说明编译器在类中自动添加了一个8字节的成员变量,这个变量就是__vptr,指向虚函数表的指针。
_vptr在对象内存中的位置应该和编译环境有关,本次测试使用gcc version 4.8.4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 test_vptr.hpp class Base { public : virtual void vFunc1 () { PRINT_INFO ("Base::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("Base::vFunc2()" ); } void Func1 () { PRINT_INFO ("Base::Func1()" ); } void Func2 () { PRINT_INFO ("Base::Func2()" ); } int m_data; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 test_vptr.cpp Base base; char * p1 = reinterpret_cast <char * >(&base); char * p2 = reinterpret_cast <char * >(&base.m_data); if (p1 == p2) { PRINT_INFO ("_vptr is in the end of class instance!" ); } else { PRINT_INFO ("_vptr is in the head of class instance!" ); } PRINT_INFO ("Instance base head point address: " << &base); PRINT_INFO ("Instance base m_data point address: " << &base.m_data);
输出结果:
[INFO]: _vptr is in the head of class instance!
[INFO]: Instance base head point address: 0x7fff18fe77e0
[INFO]: Instance base m_data point address: 0x7fff18fe77e8
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 test_vptr.cpp Base base1; char * p3 = reinterpret_cast <char * >(&base1); Base* new_base = new Base; long * pBaseVptr = (long *)(&base); long * baseVptr = (long *)(*pBaseVptr); long * pBase1Vptr = (long *)(&base1); long * base1Vptr = (long *)(*pBase1Vptr); long * pNewBaseVptr = (long *)new_base; long * newBaseVptr = (long *)(*pNewBaseVptr); PRINT_INFO ("Instance base _vptr address: " << baseVptr); PRINT_INFO ("Instance base1 _vptr address: " << base1Vptr); PRINT_INFO ("Instance new_base _vptr address: " << newBaseVptr); delete new_base;
输出结果:
[INFO]: Instance base _vptr address: 0x401d30
[INFO]: Instance base1 _vptr address: 0x401d30
[INFO]: Instance new_base _vptr address: 0x401d30
- [X] 一般继承(无虚函数覆盖) 假如有如下的继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Base { public : virtual void vFunc1 () { PRINT_INFO ("Base::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("Base::vFunc2()" ); } }; class Derived : public Base{ public : virtual void vFunc3 () { PRINT_INFO ("Derived::vFunc3()" ); } virtual void vFunc4 () { PRINT_INFO ("Derived::vFunc4()" ); } };
在子类的实例中,其虚函数表如下所示:
从上边可以看出:
虚函数按照其声明顺序放于表中; 父类的虚函数在子类的虚函数前面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 typedef void (*func) () ;Derived d; long * pvptr = (long *)(&d); long * vptr = (long *)(*pvptr); func func1 = (func)vptr[0 ]; func func2 = (func)vptr[1 ]; func func3 = (func)vptr[2 ]; func func4 = (func)vptr[3 ]; func1 ();func2 ();func3 ();func4 ();
输出结果:
[INFO]: Base::vFunc1()
[INFO]: Base::vFunc2()
[INFO]: Derived::vFunc3()
[INFO]: Derived::vFunc4()
- [X] 一般继承(有虚函数覆盖) 假如有如下继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 class Base { public : virtual void vFunc1 () { PRINT_INFO ("Base::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("Base::vFunc2()" ); } }; class Derived : public Base{ public : void vFunc1 () { PRINT_INFO ("Derived::vFunc1()" ); } void vFunc2 (int i) { PRINT_INFO ("Derived::vFunc2()" ); } };
为了看到继承之后的效果,在上述继承关系中,只覆盖了父类的一个函数:vFunc1(); 所以子类的实例,其虚函数表会是下边这个样子:
从上边可以看出:
覆盖的函数vFunc1()被放到了虚表中原来父类虚函数vFunc1()的位置,没有被覆盖的函数依旧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 typedef void (*func) () ;Derived d; long * pvptr = (long *)(&d); long * vptr = (long *)(*pvptr); func func1 = (func)vptr[0 ]; func func2 = (func)vptr[1 ]; func1 ();func2 ();PRINT_INFO ("--------------------" );Base* b = &d; b->vFunc1 (); b->vFunc2 ();
输出结果:
[INFO]: Derived::vFunc1()
[INFO]: Base::vFunc2()
[INFO]: --------------------
[INFO]: Derived::vFunc1()
[INFO]: Base::vFunc2()
由b所指的内存中的虚函数表的vFunc1()的位置已经被Derived::vFunc1()函数地址所取代,于是在实际调用发生时,是Derived::vFunc1()被调用了,这就实现了多态。
再看一种情况:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 class A { public : A (int data1, int data2) : m_data1 (data1), m_data2 (data2) { } virtual void vFunc1 () { PRINT_INFO ("A::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("A::vFunc2()" ); } void Func1 () { PRINT_INFO ("A::Func1()" ); } void Func2 () { PRINT_INFO ("A::Func2()" ); } private : int m_data1; int m_data2; }; class B : public A{ public : B (int data1) : A (data1 + 1 , data1 + 2 ), m_data3 (data1) { } void vFunc1 () { PRINT_INFO ("B::vFunc1()" ); } void Func1 () { PRINT_INFO ("B::Func1()" ); } private : int m_data3; }; class C : public B{ public : C (int data1, int data4) : B (data1 + 1 ), m_data1 (data1), m_data4 (data4) { } void vFunc2 () { PRINT_INFO ("C::vFunc2()" ); } void Func2 () { PRINT_INFO ("C::Func2()" ); } private : int m_data1; int m_data4; };
类A是基类,类B继承类A,类C又继承类B。类A,类B,类C,其对象模型如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 A a (5 ,10 ) ;long * pvptr_a = (long *)(&a); long * vptr_a = (long *)(*pvptr_a); func a_vfun1 = (func)vptr_a[0 ]; func a_vfun2 = (func)vptr_a[1 ]; a_vfun1 ();a_vfun2 ();int a_m_data1 = (int )(*(pvptr_a + 1 )); int a_m_data2 = (int )(*(((int *)(pvptr_a + 1 )) + 1 )); PRINT_INFO ("Instance a A m_data1 = " << a_m_data1);PRINT_INFO ("Instance a A m_data2 = " << a_m_data2);PRINT_INFO ("-----------------------------------" );B b (15 ) ;long * pvptr_b = (long *)(&b); long * vptr_b = (long *)(*pvptr_b); func b_vfun1 = (func)vptr_b[0 ]; func b_vfun2 = (func)vptr_b[1 ]; b_vfun1 ();b_vfun2 ();int ba_m_data1 = (int )(*(pvptr_b + 1 )); int ba_m_data2 = (int )(*(((int *)(pvptr_b + 1 )) + 1 )); int b_m_data3 = (int )(*(((int *)(pvptr_b + 1 )) + 2 ));PRINT_INFO ("Instance b A m_data1 = " << ba_m_data1);PRINT_INFO ("Instance b A m_data2 = " << ba_m_data2);PRINT_INFO ("Instance b B m_data3 = " << b_m_data3);PRINT_INFO ("-----------------------------------" );C c (20 , 30 ) ;long * pvptr_c = (long *)(&c); long * vptr_c = (long *)(*pvptr_c); func c_vfun1 = (func)vptr_c[0 ]; func c_vfun2 = (func)vptr_c[1 ]; c_vfun1 ();c_vfun2 ();int ca_m_data1 = (int )(*(pvptr_c + 1 )); int ca_m_data2 = (int )(*(((int *)(pvptr_c + 1 )) + 1 )); int cb_m_data3 = (int )(*(((int *)(pvptr_c + 1 )) + 2 ));int c_m_data1 = (int )(*(((int *)(pvptr_c + 1 )) + 3 ));int c_m_data4 = (int )(*(((int *)(pvptr_c + 1 )) + 4 )); PRINT_INFO ("Instance c A m_data1 = " << ca_m_data1);PRINT_INFO ("Instance c A m_data2 = " << ca_m_data2);PRINT_INFO ("Instance c B m_data3 = " << cb_m_data3);PRINT_INFO ("Instance c C m_data1 = " << c_m_data1);PRINT_INFO ("Instance c C m_data4 = " << c_m_data4);PRINT_INFO ("-----------------------------------" );A* pa_b = &b; pa_b->vFunc1 (); pa_b->vFunc2 (); pa_b->Func1 (); pa_b->Func2 (); PRINT_INFO ("-----------------------------------" );A* pa_c = &c; pa_c->vFunc1 (); pa_c->vFunc2 (); pa_c->Func1 (); pa_c->Func2 (); PRINT_INFO ("-----------------------------------" );B* pb_c = &c; pb_c->vFunc1 (); pb_c->vFunc2 (); pb_c->Func1 (); pb_c->Func2 (); PRINT_INFO ("-----------------------------------" );
输出结果:
[INFO]: A::vFunc1()
[INFO]: A::vFunc2()
[INFO]: Instance a A m_data1 = 5
[INFO]: Instance a A m_data2 = 10
[INFO]: -----------------------------------
[INFO]: B::vFunc1()
[INFO]: A::vFunc2()
[INFO]: Instance b A m_data1 = 16
[INFO]: Instance b A m_data2 = 17
[INFO]: Instance b B m_data3 = 15
[INFO]: -----------------------------------
[INFO]: B::vFunc1()
[INFO]: C::vFunc2()
[INFO]: Instance c A m_data1 = 22
[INFO]: Instance c A m_data2 = 23
[INFO]: Instance c B m_data3 = 21
[INFO]: Instance c C m_data1 = 20
[INFO]: Instance c C m_data4 = 30
[INFO]: -----------------------------------
[INFO]: B::vFunc1()
[INFO]: A::vFunc2()
[INFO]: A::Func1()
[INFO]: A::Func2()
[INFO]: -----------------------------------
[INFO]: B::vFunc1()
[INFO]: C::vFunc2()
[INFO]: A::Func1()
[INFO]: A::Func2()
[INFO]: -----------------------------------
[INFO]: B::vFunc1()
[INFO]: C::vFunc2()
[INFO]: B::Func1()
[INFO]: A::Func2()
[INFO]: -----------------------------------
对象的虚表指针用来指向自己所属类的虚表,虚表中的指针会指向其继承的最近的一个类 的虚函数。
- [X] 多重继承(无虚函数覆盖) 假如有如下继承关系:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 class A { public : virtual void vFunc1 () { PRINT_INFO ("A::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("A::vFunc2()" ); } }; class B { public : virtual void vFunc1 () { PRINT_INFO ("B::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("B::vFunc2()" ); } }; class C { public : virtual void vFunc1 () { PRINT_INFO ("C::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("C::vFunc2()" ); } }; class Derived : public A, public B, public C{ public : virtual void vFunc3 () { PRINT_INFO ("Derived::vFunc3()" ); } };
对于子类实例中的虚函数表,如下所示:
从上边可以看出:
每个父类都有自己的虚表; 子类的虚函数被放到了第一个父类的表中(所谓的第一个父类是按照声明顺序来判断的)。 这样做就是为了解决不同的父类类型的指针指向同一个子类实例,而能够调用到实际的函数。
PS: 上述“放到第一个父类的表中”描述不太准确,类Derived可以理解为有三张虚表(个人理解); 三张虚表中分别存在这A,B,C的虚函数地址,但是这三张表不是A,B,C的三张表,请看如下证明。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 typedef void (*func) () ;Derived d; long * pvptr_a = (long *)(&d); long * vptr_a = (long *)(*pvptr_a); func func1 = (func)vptr_a[0 ]; func func2 = (func)vptr_a[1 ]; func func3 = (func)vptr_a[2 ]; long * pvptr_b = (long *)((long *)(&d) + 1 ); long * vptr_b = (long *)(*pvptr_b); func func4 = (func)vptr_b[0 ]; func func5 = (func)vptr_b[1 ]; long * pvptr_c = (long *)((long *)(&d) + 2 ); long * vptr_c = (long *)(*pvptr_c); func func6 = (func)vptr_c[0 ]; func func7 = (func)vptr_c[1 ]; func1 (); func2 (); func3 (); func4 (); func5 (); func6 (); func7 (); PRINT_INFO ("--------------------" );A a; B b; C c; long * pvptr_aa = (long *)(&a); long * vptr_aa = (long *)(*pvptr_aa); func func8 = (func)vptr_aa[0 ]; func func9 = (func)vptr_aa[1 ]; long * pvptr_bb = (long *)(&b); long * vptr_bb = (long *)(*pvptr_bb); func func10 = (func)vptr_bb[0 ]; func func11 = (func)vptr_bb[1 ]; long * pvptr_cc = (long *)(&c); long * vptr_cc = (long *)(*pvptr_cc); func func12 = (func)vptr_cc[0 ]; func func13 = (func)vptr_cc[1 ]; func8 (); func9 (); func10 (); func11 (); func12 (); func13 (); PRINT_INFO ("--------------------" );PRINT_INFO ("Class A vptr address: " << vptr_aa);PRINT_INFO ("Class B vptr address: " << vptr_bb);PRINT_INFO ("Class C vptr address: " << vptr_cc);PRINT_INFO ("Class Derived 'A' vptr address: " << vptr_a);PRINT_INFO ("Class Derived 'B' vptr address: " << vptr_b);PRINT_INFO ("Class Derived 'C' vptr address: " << vptr_c);PRINT_INFO ("Class Derived A::vFunc1() address: " << (long *)func1);PRINT_INFO ("Class Derived A::vFunc2() address: " << (long *)func2);PRINT_INFO ("Class Derived Derived::vFunc3() address: " << (long *)func3);PRINT_INFO ("Class Derived B::vFunc1() address: " << (long *)func4);PRINT_INFO ("Class Derived B::vFunc2() address: " << (long *)func5);PRINT_INFO ("Class Derived C::vFunc1() address: " << (long *)func6);PRINT_INFO ("Class Derived C::vFunc2() address: " << (long *)func7);PRINT_INFO ("Class A A::vFunc1() address: " << (long *)func8);PRINT_INFO ("Class A A::vFunc2() address: " << (long *)func9);PRINT_INFO ("Class B B::vFunc1() address: " << (long *)func10);PRINT_INFO ("Class B B::vFunc2() address: " << (long *)func11);PRINT_INFO ("Class C C::vFunc1() address: " << (long *)func12);PRINT_INFO ("Class C C::vFunc2() address: " << (long *)func13);Derived1 d1; A* pa = &d1; pa->vFunc1 (); pa->vFunc2 (); B* pb = &d1; pb->vFunc1 (); pb->vFunc2 (); C* pc = &d1; pc->vFunc1 (); pc->vFunc2 ();
输出结果:
[INFO]: A::vFunc1()
[INFO]: A::vFunc2()
[INFO]: Derived::vFunc3()
[INFO]: B::vFunc1()
[INFO]: B::vFunc2()
[INFO]: C::vFunc1()
[INFO]: C::vFunc2()
[INFO]: --------------------
[INFO]: A::vFunc1()
[INFO]: A::vFunc2()
[INFO]: B::vFunc1()
[INFO]: B::vFunc2()
[INFO]: C::vFunc1()
[INFO]: C::vFunc2()
[INFO]: --------------------
[INFO]: Class A vptr address: 0x602d30
[INFO]: Class B vptr address: 0x602d10
[INFO]: Class C vptr address: 0x602cf0
[INFO]: Class Derived 'A' vptr address: 0x602c70
[INFO]: Class Derived 'B' vptr address: 0x602c98
[INFO]: Class Derived 'C' vptr address: 0x602cb8
[INFO]: Class Derived A::vFunc1() address: 0x401678
[INFO]: Class Derived A::vFunc2() address: 0x4016be
[INFO]: Class Derived Derived::vFunc3() address: 0x40181c
[INFO]: Class Derived B::vFunc1() address: 0x401704
[INFO]: Class Derived B::vFunc2() address: 0x40174a
[INFO]: Class Derived C::vFunc1() address: 0x401790
[INFO]: Class Derived C::vFunc2() address: 0x4017d6
[INFO]: Class A A::vFunc1() address: 0x401678
[INFO]: Class A A::vFunc2() address: 0x4016be
[INFO]: Class B B::vFunc1() address: 0x401704
[INFO]: Class B B::vFunc2() address: 0x40174a
[INFO]: Class C C::vFunc1() address: 0x401790
[INFO]: Class C C::vFunc2() address: 0x4017d6
[INFO]: A::vFunc1()
[INFO]: A::vFunc2()
[INFO]: B::vFunc1()
[INFO]: B::vFunc2()
[INFO]: C::vFunc1()
[INFO]: C::vFunc2()
输出结果可以看出: 虚函数共享,虚表不共享
- [X] 多重继承(有虚函数覆盖) 假如有如下继承关系:
1 2 3 4 5 6 7 8 9 10 11 class Derived : public A, public B, public C{ public : void vFunc1 () { PRINT_INFO ("Derived::vFunc1()" ); } };
对于子类实例中的虚函数表,如下所示:
从上边可以看出:
三个父类虚函数表中的vFunc1()的位置被替换成了子类的函数指针,这样就实现了多态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 Derived d; long * pvptr_a = (long *)(&d); long * vptr_a = (long *)(*pvptr_a); func func1 = (func)vptr_a[0 ]; func func2 = (func)vptr_a[1 ]; func func3 = (func)vptr_a[2 ]; long * pvptr_b = (long *)((long *)(&d) + 1 ); long * vptr_b = (long *)(*pvptr_b); func func4 = (func)vptr_b[0 ]; func func5 = (func)vptr_b[1 ]; long * pvptr_c = (long *)((long *)(&d) + 2 ); long * vptr_c = (long *)(*pvptr_c); func func6 = (func)vptr_c[0 ]; func func7 = (func)vptr_c[1 ]; func1 (); func2 (); func3 (); func4 (); func5 (); func6 (); func7 (); PRINT_INFO ("---------------------------" );A* pa = &d; pa->vFunc1 (); pa->vFunc2 (); B* pb = &d; pb->vFunc1 (); pb->vFunc2 (); C* pc = &d; pc->vFunc1 (); pc->vFunc2 (); func pa_vFunc3 = (func)(((long *)(*(long *)pa))[2 ]); pa_vFunc3 ();
输出结果:
[INFO]: Derived::vFunc1()
[INFO]: A::vFunc2()
[INFO]: Derived::vFunc3()
[INFO]: Derived::vFunc1()
[INFO]: B::vFunc2()
[INFO]: Derived::vFunc1()
[INFO]: C::vFunc2()
[INFO]: ---------------------------
[INFO]: Derived::vFunc1()
[INFO]: A::vFunc2()
[INFO]: Derived::vFunc1()
[INFO]: B::vFunc2()
[INFO]: Derived::vFunc1()
[INFO]: C::vFunc2()
[INFO]: Derived::vFunc3()
虚析构函数 C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。
所以,虚析构函数也是在虚函数表中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 class Base { public : Base () { PRINT_INFO ("Base::Base()" ); } virtual ~Base () { PRINT_INFO ("Base::~Base()" ); } virtual void vFunc1 () { PRINT_INFO ("Base::vFunc1()" ); } virtual void vFunc2 () { PRINT_INFO ("Base::vFunc2()" ); } }; class Derived : public Base{ public : Derived () { PRINT_INFO ("Derived::Derived()" ); } ~Derived () { PRINT_INFO ("Derived::~Derived()" ); } }; typedef void (*func) () ;Derived d; func func1 = (func)(((long *)(*((long *)(&d))))[0 ]); func func2 = (func)(((long *)(*((long *)(&d))))[1 ]); func func3 = (func)(((long *)(*((long *)(&d))))[2 ]); func func4 = (func)(((long *)(*((long *)(&d))))[3 ]); func1 ();func3 ();func4 ();
输出结果:
[INFO]: Base::Base()
[INFO]: Derived::Derived()
[INFO]: Derived::~Derived()
[INFO]: Base::~Base()
[INFO]: Base::vFunc1()
[INFO]: Base::vFunc2()
[INFO]: Derived::~Derived()
[INFO]: Base::~Base()
输出结果显示,析构函数被调用了两次,第一次是通过func1()调用的,后边是正常释放对象d。 上述func1()函数调用的打印说明,在调用Derived析构函数的时候也执行了Base的析构函数,所以在Derived析构函数中调用了Base的析构函数。
1 2 3 4 5 6 PRINT_INFO ("---------------------" );Base* pb = new Derived; delete pb;PRINT_INFO ("---------------------" );
输出结果:
[INFO]: ---------------------
[INFO]: Base::Base()
[INFO]: Derived::Derived()
[INFO]: Derived::~Derived()
[INFO]: Base::~Base()
[INFO]: ---------------------
如果Base的析构函数不是virtual的,则输出结果是只会打印Base::~Base()。
基类指针指向了派生类对象,而基类中的析构函数如果是非virtual的,之前讲过,虚函数是动态绑定的基础。现在析构函数不是virtual的,因此不会发生动态绑定,而是静态绑定,指针的静态类型为基类指针,因此在delete时候只会调用基类的析构函数,而不会调用派生类的析构函数。
经过虚表调用虚函数的过程称为动态绑定,其表现出来的现象称为运行时多态。动态绑定区别于传统的函数调用,传统的函数调用我们称之为静态绑定,即函数的调用在编译阶段就可以确定下来了。
纯虚函数 包含纯虚函数的类称之为抽象类。 对于抽象类来说,它无法实例化对象,而对于抽象类的子类来说,只有把抽象类中的纯虚函数全部实现之后,子类才可以实例化对象。
安全性 C++的不安全性,用虚函数表来违反C++语义的行为
通过父类型的指针,访问子类自己的虚函数 子类没有覆盖父类的虚函数是一件毫无意义的事情。因为多态也是要基于函数重写的。 虽然从上面我们可以看到A的虚表中有Derived的虚函数,但我们根本不可能使用下面的语句来调用子类的自有虚函数:
1 2 3 4 Derived d; A* pa = &d; pa->vFunc3 ();
任何妄图使用父类指针想调用子类中的未覆盖父类的成员函数的行为都会被编译器视为非法,所以,这样的程序根本无法编译通过。但在运行时,我们可以通过指针的方式访问虚函数表来达到违反C++语义的行为。
1 2 3 4 typedef void (*func) () ;func pa_vFunc3 = (func)(((long *)(*(long *)pa))[2 ]); pa_vFunc3 ();
访问non-public的虚函数 如果父类的虚函数是private或是protected的,但这些非public的虚函数同样会存在于虚函数表中,所以,我们同样可以使用访问虚函数表的方式来访问这些non-public的虚函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 class Base { private : virtual void vFunc1 () { PRINT_INFO ("private Base::vFunc1()" ); } protected : virtual void vFunc2 () { PRINT_INFO ("protected Base::vFunc2()" ); } }; class Derived : public Base{ }; Derived d; long * pvptr = (long *)(&d);long * vptr = (long *)(*pvptr);func func1 = (func)vptr[0 ]; func func2 = (func)vptr[1 ]; func1 ();func2 ();
输出结果:
[INFO]: private Base::vFunc1()
[INFO]: protected Base::vFunc2()