• C++虚函数表


    C++通过虚函数来实现多态,也就是让父类指针指向子类,在运行时根据指针指向的对象的实际类型来确定调用子类或者父类中的某个函数。

     1 class Base
     2 {
     3 public :
     4     virtual void f() { cout<<"Base::f()"<<endl ; }
     5     virtual void g() { cout<<"Base::g()"<<endl ; }
     6     virtual void h() { cout<<"Base::h()"<<endl ; }
     7 } ;
     8 
     9 class Derive : public Base
    10 {
    11 public :
    12     void f() { cout<<"Derive::f()"<<endl ; }
    13     virtual void g() { cout<<"Derive::g()"<<endl ; }
    14     virtual void i() { cout<<"Derive::i()"<<endl ; }
    15     void j() { cout<<"Derive::j()"<<endl ; }
    16 } ;
    17 
    18 int main()
    19 {
    20     Base* b = new Derive() ;
    21     b->f()  ;
    22     b = new Base() ;
    23     b->f() ;
    24     return 0 ;
    25 }

    上面定义了父类 Base 和它的子类 Derive ,定义了父类类型的指针,使其先后指向一个Derive对象和Base对象,调用f().输出如下:

    1 Derive::f()

    那是如何确定调用哪个函数的呢?为了确定正确的调用函数,一种常用的实现技术是让编译器把一个virtual函数的名字转换为指向这些函数的指针表的一个下标(引用自C++程序设计语言),这个表就是虚函数表(简称vtb),vtb是一个静态的一维数组,数组元素是函数指针, 通过这些指针可以调用对应的函数。

    vtb的地址一般保存在对象的最开始的位置,就拿上面的代码举例,Base类型对象的vtb的地址为

    1 typedef void(*fun)(void)
    2 
    3 Base b ;
    4 
    5 (long*)*(long*)&b//对象 b 的 vtb 的地址

    从而可以通过vtb表调用函数

    cout<<"address of vtb's address"<<(long*)&b<<endl ;
    cout<<"address of vtb"<<(long*)*(long*)&b<<endl ;
    cout<<"address of first function in object"<<(fun)*(long*)*(long*)&b<<endl ;
    
    ((fun)*(long*)*(long*)&b)() ;
    ((fun)*((long*)*(long*)&b+1))() ;
    ((fun)*((long*)*(long*)&b+2))() ;

    需要注意的是,vtb中函数地址的次序与类中函数声明的次序是一致的,对于子类,父类的函数地址在子类的前面,如果子类重写了父类的虚函数,不管重写后的函数是否还是虚函数,也不管子类的这个函数声明在什么位置,在子类对象的vtb中,该重写的函数的地址会取代父类被重写函数的地址。

    当出现多继承时,子类对象会有多个vtb,在对象开始处也会有多个vtb表的地址,即对象开始处也有一个一维数组,数组元素是vtb表的地址。

     1 #include<iostream>
     2 
     3 using namespace std ;
     4 
     5 class Base1
     6 {
     7 public :
     8     virtual void f() { cout<<"Base1::f()"<<endl ; }
     9     virtual void g() { cout<<"Base1::g()"<<endl ; }
    10     virtual void h() { cout<<"Base1::h()"<<endl ; }
    11     virtual void b1() { cout<<"Base::b1()"<<endl ; }
    12 } ;
    13 
    14 class Base2
    15 {
    16 public :
    17     virtual void f() { cout<<"Base2::f()"<<endl ; }
    18     virtual void g() { cout<<"Base2::g()"<<endl ; }
    19     virtual void h() { cout<<"Base2::h()"<<endl ; }
    20     virtual void b2() { cout<<"Base::b2()"<<endl ; }
    21 } ;
    22 
    23 class Base3
    24 {
    25 public :
    26     virtual void f() { cout<<"Base3::f()"<<endl ; }
    27     virtual void g() { cout<<"Base3::g()"<<endl ; }
    28     virtual void h() { cout<<"Base3::h()"<<endl ; }
    29     virtual void b3() { cout<<"Base::b3()"<<endl ; }
    30 } ;
    31 
    32 class Derive : public Base1 , Base2 , Base3
    33 {
    34 public :
    35     void f() { cout<<"Derive::f()"<<endl ; }
    36     virtual void g() { cout<<"Derive::g()"<<endl ; }
    37     virtual void i() { cout<<"Derive::i()"<<endl ; }
    38     void j() { cout<<"Derive::j()"<<endl ; }
    39     void b1() { cout<<"Derive::b1()"<<endl ; }
    40     void b2() { cout<<"Derive::b2()"<<endl ; }
    41     void b3() { cout<<"Derive::b3()"<<endl ; }
    42 } ;
    43 
    44 typedef void(*fun)(void) ;
    45 
    46 int main()
    47 {
    48     Derive d = Derive() ;
    49     
    50     cout<<"address of vtb1's address:"<<(long*)&d<<endl ;
    51     cout<<"address vtb1:"<<(long*)*(long*)&d<<endl ;
    52     cout<<"address of first function in vtb1:"<<(fun)*(long*)*(long*)&d<<endl ;
    53     ((fun)*((long*)*(long*)&d + 0))() ;
    54     ((fun)*((long*)*(long*)&d + 1))() ;
    55     ((fun)*((long*)*(long*)&d + 2))() ;
    56     ((fun)*((long*)*(long*)&d + 3))() ;
    57     ((fun)*((long*)*(long*)&d + 4))() ;
    58     ((fun)*((long*)*(long*)&d + 5))() ;
    59     ((fun)*((long*)*(long*)&d + 6))() ;
    60     //((fun)*((long*)*(long*)&d + 7))() ;
    61     
    62     cout<<"address of vtb2's address:"<<(long*)&d<<endl ;
    63     cout<<"address vtb2:"<<(long*)*(long*)&d<<endl ;
    64     cout<<"address of first function in vtb2:"<<(fun)*(long*)*(long*)&d<<endl ;
    65     ((fun)*((long*)*((long*)&d + 1) + 0))() ;
    66     ((fun)*((long*)*((long*)&d + 1) + 1))() ;
    67     ((fun)*((long*)*((long*)&d + 1) + 2))() ;
    68     ((fun)*((long*)*((long*)&d + 1) + 3))() ;
    69     
    70     cout<<"address of vtb3's address:"<<(long*)&d<<endl ;
    71     cout<<"address vtb3:"<<(long*)*(long*)&d<<endl ;
    72     cout<<"address of first function in vtb3:"<<(fun)*(long*)*(long*)&d<<endl ;
    73     ((fun)*((long*)*((long*)&d + 2) + 0))() ;
    74     ((fun)*((long*)*((long*)&d + 2) + 1))() ;
    75     ((fun)*((long*)*((long*)&d + 2) + 2))() ;
    76     ((fun)*((long*)*((long*)&d + 2) + 3))() ;
    77     
    78     return 0 ;
    79 }

    参考:http://blog.csdn.net/haoel/article/details/1948051

  • 相关阅读:
    MFC/HALCON混合编程系列三_CFielDialog打开文件对话框
    MFC/HALCON混合编程系列二_打开两幅图_MFC布局_
    MFC/HALCON混合编程系列一_相机打开图像_简单处理_
    MFC C++ Cstring与string互转
    ImageMagik——开发篇(转)
    二维码解码器Zbar+VS2010开发环境配置(使用opencv库)
    select @@Identity 返回自动递增字段的值
    WebStorm设置左侧菜单栏背景和字体设置
    Chrome插件推荐
    WebStorm常用快捷键
  • 原文地址:https://www.cnblogs.com/yang-wen/p/6554700.html
Copyright © 2020-2023  润新知