zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    layUI table.reload 刷新表格
    js实现 StringBuilder
    sqlServer 重复数据项处理,只选其中一条,保留一条
    在唯一密钥属性“name”设置为“XXX”时,无法添加类型为“add”的重复集合项
    sqlserver 把 某一列的所有值 变成 in 里面的内容
    初识第三方登录
    卸载vsto插件的方法
    layui-框架学习小总结
    c# 记一次批量获取自己的qq好友的CF游戏战绩
    得到qq正在登录的qq的号
  • 原文地址:https://www.cnblogs.com/yang-wen/p/6554700.html
Copyright © 2011-2022 走看看