C++对象模型探索-2

  • C++对象模型探索2
  • 补充C++对象模型探索1

25 两层结构时虚基类表内容分析

  • 1564645591473
  • 虚基类表内容之5-8字节内容分析

    • 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
      class Grand {
      public:
      int m_grand;
      };

      class A1 :public virtual Grand {
      public:
      int m_a1;
      };
      class A2 :public virtual Grand {
      public:
      int m_a2;
      };
      class C1 :public A1, public A2 {
      public:
      int m_c1;
      };
      A1 a1;//+ &a1 0x0095fb5c {m_a1=-858993460 } A1 *

      a1.m_grand = 2;
      a1.m_a1 = 3;
      /*
      sizeof(Grand) 4
      sizeof(A1) 12
      sizeof(A2) 12
      sizeof(C1) 24

      A1 前四个字节为vbptr &a1=0x0095fb5c
      0x0095FB5C 30 8b 98 00 cc cc cc cc 02 00 00 00
      虚函数表地址为0x00988b30
      0x00988B30 00 00 00 00 08 00 00 00

      a1.m_grand = 2;
      009825C2 mov eax,dword ptr [a1] //把a1 地址赋值给EAX
      009825C5 mov ecx,dword ptr [eax+4] 把虚基类表中后4个字节=8 赋值给ECX
      009825C8 mov dword ptr a1[ecx],2 2赋值给 a1+8

      */
    • 一般虚基类表8字节,四个字节为一个单位,每多一个虚基类Grand,虚基类表多加4个字节

    • 虚基类,编译器为A1 A2提供默认构造函数,增加代码,给vbptr赋值

    • 虚基类表指针即为成员变量的首地址+这个偏移量=虚基类对象首地址。跳过这个偏移量,访问虚基类对象

    • 虚基类表中5-8字节存储的是A1访问虚基类对象Grand的成员变量的偏移量

    • 虚基类表编译时生成,多次运行相同,虚基类表指针相同

  • 各种形色继承

    • 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
      class Grand {
      public:
      int m_grand;
      };
      class Grand2 {
      public:
      int m_grand2;
      };

      class A1 :public virtual Grand , public Grand2 {
      public:
      int m_a1;
      };
      A1 a1;
      a1.m_grand = 2;//13-16
      a1.m_a1 = 3;//9-12
      a1.m_grand2 = 4;//1-4
      //虚基类表指针5-8
      /*
      sizeof(Grand) 4
      sizeof(A1) 16
      sizeof(A2) 12
      0x00BCFCB0 04 00 00 00 30 8b e8 00 03 00 00 00 02 00 00 00
      0x00E88B30 fc ff ff ff 08 00 00 00 //虚函数表前四个字节为-4,后四个字节为8

      */
    • 1564647268839

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      class Grand {
      public:
      int m_grand;
      };
      class Grand2 {
      public:
      int m_grand2;
      };

      class A1 :public Grand ,virtual public Grand2 {
      public:
      int m_a1;
      };

      A1 a1;
      a1.m_grand = 2;//1-4
      a1.m_a1 = 3;//9-12
      a1.m_grand2 = 4;//13-16
      //虚基类表指针5-8
      /*
      0x012FFD58 02 00 00 00 30 8b d7 00 03 00 00 00 04 00 00 00
      0x00D78B30 fc ff ff ff 08 00 00 00 00 00 00 00
      */
    • 1564647899087

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      class Grand {
      public:
      int m_grand;
      };
      class Grand2 {
      public:
      int m_grand2;
      };

      class A1 :virtual public Grand ,virtual public Grand2 {
      public:
      int m_a1;
      };
      A1 a1;

      a1.m_grand = 2;
      a1.m_a1 = 3;
      a1.m_grand2 = 4;

      //0x0098F8D8 30 8b 87 00 03 00 00 00 02 00 00 00 04 00 00 00
      //虚基类表
      //0x00878B30 00 00 00 00 08 00 00 00 0c 00 00 00 //8 为 m_grand偏移量 m_grand2=12 为m_grand2偏移量,相对于虚基类表头部的偏移量
    • 1564648316374

    • 实继承的 成员会在最上面,虚继承的在最下面,两个都虚继承的话,只有一个虚基类表指针(同虚函数不同),按照继承顺序,先继承的倒数第二,后继承的倒数第一

    • 虚基类表3项 +4 +8,通过取得虚基类表中偏移值来赋值

    • 虚基类表中偏移量,按照继承顺序存放

    • 虚基类子对象一直放在最下面,按照继承顺序

    • 实继承成员+虚继承的虚基类表指针+本身的成员+虚基类的成员

    • 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
      class Grand {
      public:
      int m_grand;
      };
      class Grand2 {
      public:
      int m_grand2;
      int m_grand2_1;
      };

      class A1 :virtual public Grand ,virtual public Grand2 {
      public:
      int m_a1;
      };
      A1 a1;

      a1.m_grand = 2;
      a1.m_a1 = 3;
      a1.m_grand2 = 4;
      a1.m_grand2_1 = 5;

      /*
      a1.m_grand2 = 4;
      009B259E mov ecx,dword ptr [eax+8]
      009B25A1 mov dword ptr a1[ecx],4
      a1.m_grand2_1 = 5;
      009B25A9 mov eax,dword ptr [a1]
      009B25AC mov ecx,dword ptr [eax+8]
      009B25AF mov dword ptr [ebp+ecx-18h],5

      0x00CFF9E0 30 8b 9b 00 03 00 00 00 02 00 00 00 04 00 00 00 05 00 00 00
      0x009B8B30 00 00 00 00 08 00 00 00 0c 00 00 00
      */
    • 虚基类表存放着 虚基类对象的首地址相对于虚基类表指针的偏移量,对象地址+虚基类表中虚基类对象相对于虚基类表指针的偏移量=虚基类对象的首地址

  • 虚基类表内容之1-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
      class Grand {
      public:
      int m_grand;
      };
      class Grand2 {
      public:
      int m_grand2;
      int m_grand2_1;
      };

      class A1 : public Grand ,virtual public Grand2 {
      public:
      int m_a1;
      };

      A1 a1;

      a1.m_grand = 2;
      a1.m_a1 = 3;
      a1.m_grand2 = 4;
      a1.m_grand2_1 = 5;
      /*
      0x012FFD58 02 00 00 00 30 8b d7 00 03 00 00 00 04 00 00 00 05 00 00 00
      0x00D78B30 fc ff ff ff 08 00 00 00 00 00 00 00
      */
    • 虚基类表指针成员变量的首地址,和本对象A1首地址之间的偏移量也就是虚基类表指针的首地址-A1对象的首地址=-4

    • 只有对虚基类成员进行处理,才会用到虚基类表表,取其中的偏移进行计算

26 三层结构时虚基类表内容分析

  • 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
    class Grand {
    public:
    int m_grand;
    };


    class A1 : virtual public Grand {
    public:
    int m_a1;
    };
    class A2 :public virtual Grand {
    public:
    int m_a2;
    };
    class C1 :public A1, public A2 {
    public:
    int m_c1;
    };

    cout << "sizeof(Grand) " << sizeof(Grand) << endl;
    cout << "sizeof(A1) " << sizeof(A1) << endl;
    cout << "sizeof(A2) " << sizeof(A2) << endl;
    // cout << "sizeof(C1) " << sizeof(C1) << endl;//8 两份grand

    C1 c1;
    c1.m_c1 = 1;//17-20
    c1.m_a1 = 2;//5-8
    c1.m_a2 = 3;//13-16
    c1.m_grand = 4;//
    //vptr1 1-4
    //vptr2 9-12
    /*
    sizeof(Grand) 4
    sizeof(A1) 12
    sizeof(A2) 12
    sizeof(C1) 24
    &c1=0x00BDFDA0 50 8b 79 00 02 00 00 00 60 8b 79 00 03 00 00 00 01 00 00 00 04 00 00 00
    0x00798B50 00 00 00 00 14 00 00 00
    0x00798B60 00 00 00 00 0c 00 00 00

    C1 c1;
    00795DF8 push 1
    00795DFA lea ecx,[c1]
    00795DFD call C1::C1 (07913E3h)
    c1.m_c1 = 1;
    00795E02 mov dword ptr [ebp-10h],1
    c1.m_a1 = 2;
    00795E09 mov dword ptr [ebp-1Ch],2
    c1.m_a2 = 3;
    00795E10 mov dword ptr [ebp-14h],3

    c1.m_grand = 4;
    00795E17 mov eax,dword ptr [c1] //c1首地址为虚基类表指针
    00795E1A mov ecx,dword ptr [eax+4] 十六进制14 为10进制20
    00795E1D mov dword ptr c1[ecx],4

    c1.m_grand2 = 5;
    00A95E25 mov eax,dword ptr [c1]
    00A95E28 mov ecx,dword ptr [eax+4]
    00A95E2B mov dword ptr [ebp+ecx-20h],5

    */
  • 1565854078004

  • 没有用到VBPTR2,只保留了一份grand

  • 访问虚基类的成员变量比其他成员变量慢。

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    A2 *pa2 = &c1;//pa2 = 0x0133fe20 {m_a2=7 }
    pa2->m_grand = 6;
    /*
    &c1=0x0133FE18
    0x0133FE20 60 8b 12 00
    0x00128B60 00 00 00 00 0c 00 00 00
    pa2->m_grand = 6;
    00125E5B mov eax,dword ptr [pa2] pa2指向的内容给eax
    00125E5E mov ecx,dword ptr [eax]
    00125E60 mov edx,dword ptr [ecx+4]
    00125E63 mov eax,dword ptr [pa2]
    00125E66 mov dword ptr [eax+edx],6

    */

27 成员变量地址、偏移、指针重申

  • 对象成员变量内存地址及其指针

    • 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
      class MYACLS {
      public:
      int m_i;
      int m_j;
      int m_k;
      };

      MYACLS myobj;
      myobj.m_i = 1;
      myobj.m_j = 2;
      myobj.m_k = 3;



      printf("myobj.m_i= %p \n", &myobj.m_i);
      printf("myobj.m_j= %p \n", &myobj.m_j);
      printf("myobj.m_k= %p \n", &myobj.m_k);

      MYACLS *pmyobj = new MYACLS();


      printf("pmyobj.m_i= %p \n", &pmyobj->m_i);
      printf("pmyobj.m_j= %p \n", &pmyobj->m_j);
      printf("pmyobj.m_k= %p \n", &pmyobj->m_k);


      int *p1 = &myobj.m_i;
      int *p2 = &pmyobj->m_i;
      *p1 = 1;
      *p2 = 2;

      printf("p1= %p *p1=%d\n", p1,*p1);
      printf("p2= %p *p2=%d\n", p2, *p2);
      /*

      myobj.m_i= 012FFA98
      myobj.m_j= 012FFA9C
      myobj.m_k= 012FFAA0
      pmyobj.m_i= 01606DF0
      pmyobj.m_j= 01606DF4
      pmyobj.m_k= 01606DF8
      p1= 012FFA98 *p1=1
      p2= 01606DF0 *p2=2
      */
  • 成员变量的偏移值及其指针

    • 和具体对象没关系

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      printf("myobj.m_i offset = %p \n", &MYACLS::m_i);
      printf("myobj.m_j offset = %p \n", &MYACLS::m_j);
      printf("myobj.m_k offset = %p \n", &MYACLS::m_k);
      int MYACLS::*mypoint = &MYACLS::m_i;//定义时,加个类名,使用时,直接使用

      printf("&MYACLS::m_i = %p \n", mypoint);
      mypoint = &MYACLS::m_j;
      printf("&MYACLS::m_j = %p \n", mypoint);
      /*
      myobj.m_i offset = 00000000
      myobj.m_j offset = 00000004
      myobj.m_k offset = 00000008
      &MYACLS::m_i = 00000000
      &MYACLS::m_j = 00000004
      */
    • 成员变量指针,实际保存的是偏移值,相对于类开头的地址

  • 没有指向任何数据成员变量的指针

    • 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

      //成员变量指针 可以作为参数传
      void myfunc(int MYACLS::*mempoint, MYACLS &obj)
      {
      obj.*mempoint = 12;
      }

      int MYACLS::*mypoint = &MYACLS::m_i;//成员变量指针定义时,加个类名,使用时,直接使用

      printf("&MYACLS::m_i = %p \n", mypoint);//mypoint=0 成员变量指针=0,但是有意义,表示指向第一个成员变量的偏移量为0
      mypoint = &MYACLS::m_j;
      printf("&MYACLS::m_j = %p \n", mypoint);


      //通过一个对象名/对象指针 跟成员变量指针 访问成员变量
      myobj.m_i = 13;
      myobj.*mypoint = 14;

      pmyobj->*mypoint = 19;


      myfunc(mypoint,myobj);
      myfunc(mypoint, myobj);

      mypoint = 0;//直接给成员变量指针为 0时,为-1mypoint = 0xffffffff {???}-1
      mypoint = nullptr;//成员变量指针为 nullptr时,为-1 mypoint = 0xffffffff {???} -1
    • 没有指向任意有效的成员变量的成员变量指针为 -1

28 普通成员函数的调用方式

  • 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 MYACLS {
    public:
    int m_i;
    void myfunc(int abc)
    {
    m_i += abc;
    }
    };

    void gmyfunc(MYACLS* ptmp, int abc)
    {
    ptmp->m_i += abc;
    }
    void myfunc(MYACLS *const thisint abc)
    {
    this->m_i+=abc;
    }
    MYACLS myobj;
    myobj.myfunc(18);

    gmyfunc(&myobj,18);


    printf("MYACLS myfunc 地址= %p \n",&MYACLS::myfunc);

    /*

    */
    //linux 下,通过nm 5_1 查看 函数地址
  • C++要求对普通成员函数调用不应该比全局函数慢,基于这种情况,编译器内部实际将成员函数的调用转换为对全局函数的调用

  • 成员函数跟着类走,有独立的内存地址,且地址在编译时就确定了

    • 编译器对成员函数额外增加this形参,指向生成的对象
    • 对于常规成员变量的存取,都通过this形参来进行访问

29 虚函数、静态成员函数调用方式

  • 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 MYACLS {
    public:
    int m_i;
    void myfunc(int abc)
    {
    m_i += abc;
    }
    virtual void myvirfunc()
    {
    printf("myvirfunc called this= %p\n",this);
    }
    };

    MYACLS myobj;
    //myobj.myfunc(18);
    //gmyfunc(&myobj,18);

    myobj.myvirfunc();

    MYACLS * pmyacls = new MYACLS();
    pmyacls->myvirfunc();

    //(*pmyacls->vptr[0])(pmyacls)
    // vptr 虚函数表指针,
    //vptr[0] 虚函数表中第一项
    //pmyacls this指针,传递一个参数
    //* 就得到了虚函数的地址
    delete pmyacls;
  • 通过对象调用虚函数,同普通成员函数一样,不需要通过虚函数表

  • 通过指针调用虚函数,需要通过虚函数表找到虚函数的地址

  • 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
    class MYACLS {
    public:
    int m_i;
    void myfunc(int abc)
    {
    m_i += abc;
    }
    virtual void myvirfunc()
    {
    printf("myvirfunc called this= %p\n",this);
    myvirfunc2();//通过虚函数表指针调用
    MYACLS::myvirfunc2();// 直接调用,效率更高,压制了虚拟机制,不通过查询虚函数表来调用
    //通过类名调用虚函数方式,等价于直接调用一个普通函数
    }
    virtual void myvirfunc2()
    {
    printf("myvirfunc2 called this= %p\n", this);
    }

    };
    MYACLS myobj;
    //myobj.myfunc(18);
    //gmyfunc(&myobj,18);

    myobj.myvirfunc();
  • 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
    class MYACLS {
    public:
    int m_i;
    void myfunc(int abc)
    {
    m_i += abc;
    }
    virtual void myvirfunc()
    {
    printf("myvirfunc called this= %p\n",this);
    myvirfunc2();//通过虚函数表指针调用
    MYACLS::myvirfunc2();// 直接调用,效率更高,压制了虚拟机制,不通过查询虚函数表来调用
    }
    virtual void myvirfunc2()
    {
    printf("myvirfunc2 called this= %p\n", this);
    }
    static void mystfunc()
    {
    cout << "static member funciton called"<< endl;
    }
    };

    MYACLS myobj;
    myobj.mystfunc();
    MYACLS::mystfunc();

    MYACLS *pmyacls = new MYACLS();
    pmyacls->mystfunc();
    ((MYACLS*)0)->mystfunc();// 仅仅 静态成员函数,编译器没有this参数,this参数为空
    printf("MYACLS::mystfunc 静态函数地址=%p\n", &MYACLS::mystfunc);
    /*
    ((MYACLS*)0)->mystfunc();// 仅仅 静态成员函数,编译器没有this参数,this参数为空
    0100293B call MYACLS::mystfunc (01001221h)
    ((MYACLS*)0)->myfunc(12); // 异常this 是 nullptr。
    //但是 如果在myfunc中不使用this指针,也不报异常!!!!!!!!!!!!
    //因为 有其自己的地址,在编译时就已经确定了
    */
  • 静态成员函数不需要this指针

  • 指向0的指针调用成员函数。有些成员函数支持独立于类对象之外的存取操作!!!!!

  • 静态成员函数特性

    • 静态成员函数没有this指针
    • 无法直接存取类中普通非静态成员变量
    • 静态成员函数不能在后使用const,也不能virtual
    • 可以用类对象来调用,不非一定要用类调用
    • 静态成员函数等同于非成员函数,有的需要提供回调函数的场合可以将静态成员函数作为回调函数
    • 静态成员函数也有其地址

30 虚成员函数地址问题的vcall引入

  • 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
    class MYACLS {
    public:
    virtual void myvirfunc()
    {

    }
    virtual void myvirfunc2()
    {

    }

    };
    printf(" MYACLS myvirfunc =%p\n", &MYACLS::myvirfunc);//00B81145 为vcall函数地址,不是真正的虚函数地址
    printf(" MYACLS myvirfunc2 =%p\n", &MYACLS::myvirfunc2);//00B81064
    cout<<sizeof(MYACLS)<<endl;

    MYACLS *pmyobj = new MYACLS(); //pmyobj = 0x00d40578 {...}
    //__vfptr = 0x00b87b34 {class31_virtual_member_function.exe!const MYACLS::`vftable'} {0x00b811c7 {class31_virtual_member_function.exe!MYACLS::myvirfunc(void)}, ...}
    //[0] = 0x00b811c7 {class31_virtual_member_function.exe!MYACLS::myvirfunc(void)}
    //[1] = 0x00b81127 {class31_virtual_member_function.exe!MYACLS::myvirfunc2(void)}

    /*
    0x00b811c7 !=00B81145
    0x00b81127!=00B81064

    MYACLS::`vcall'{0}':
    00B81145 jmp MYACLS::`vcall'{0}' (0B81988h)
    MYACLS::`vcall'{4}':
    00B81064 jmp MYACLS::`vcall'{4}' (0B81983h)
    */
  • vcall think–多重继承中

    • 调整this指针
    • 跳转到真正的虚函数中

31 静态动态类型、绑定

  • 静态类型/动态类型

    • 静态类型,对象定义时类型,编译期间就确定好的

    • 动态类型,对象目前所指向的类型(运行时才决定的类型)–指针+引用,一般指父类指针、引用指向子类

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      class Base {
      public:

      };
      class Derive :public Base {
      public:
      };

      class Derive2 :public Base {
      public:
      };
      Base base;
      Derive derive;//derive 静态类型为Derive ,没有动态类型
      Base* pbase;//pbase 静态类型为Base* ,目前没有任何动态类型,没有指向任何对象
      Base* pbase2 = new Derive();// pbase2 的静态类型为Base*,动态类型为Derive


      pbase = pbase2;//pbase的动态类型变为Derive
  • 静态绑定/动态绑定

    • 静态绑定:绑定的静态类型,所对应的函数或者属性依赖于对象的静态类型,发生在编译期间
    • 动态绑定:绑定动态类型,所对应的函数或者属性依赖于对象的动态类型,发生在运行期间
      • 普通成员函数静态绑定,虚函数动态绑定
      • 缺省参数一般静态绑定
  • 继承的非虚函数

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      	Derive derive;
      Derive *pderive = &derive;
      pderive->myfunc();//pderive 静态类型为Derive*

      Base* pbase = &derive;
      pbase->myfunc();
      /*
      Derive myfunc
      Base myfunc
      */
    • 普通成员函数静态绑定,myfunc静态绑定,取决于调用者的静态类型

    • 不应该在子类中重新定义一个继承来的非虚函数

  • 虚函数的动态绑定

    • 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
      class Base {
      public:
      void myfunc()
      {
      cout << "Base myfunc"<< endl;
      }
      virtual void virfunc()
      {
      cout << "Base virfunc" << endl;
      }
      };
      class Derive :public Base {
      public:
      void myfunc()
      {
      cout << "Derive myfunc" << endl;
      }
      virtual void virfunc()
      {
      cout << "Derive virfunc" << endl;
      }
      };
      Base* pbase = &derive;
      pbase->myfunc();

      cout << string(10,'-')<< endl;
      Base base;
      pderive->virfunc();//动态类型为 Derive *
      pbase->virfunc();//动态类型为 Derive *,虚函数动态绑定,参照动态类型

      pbase = &base;//动态类型为 Base *
      pbase->virfunc();//

      /*
      Base myfunc
      ----------
      Derive virfunc
      Derive virfunc
      Base virfunc
      */
    • 虚函数动态绑定,取决于调用对象的动态类型

  • 重新定义虚函数的缺省参数

    • 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
      class Base {
      public:
      void myfunc()
      {
      cout << "Base myfunc"<< endl;
      }
      virtual void virfunc(int val=1)
      {
      cout << "Base virfunc val="<<val << endl;
      }
      };
      class Derive :public Base {
      public:
      void myfunc()
      {
      cout << "Derive myfunc" << endl;
      }
      virtual void virfunc(int val=2)
      {
      cout << "Derive virfunc val="<<val << endl;
      }
      };

      Base* pbase = &derive;
      pbase->myfunc();

      cout << string(10,'-')<< endl;
      Base base;
      pderive->virfunc();//动态类型为 Derive *
      pbase->virfunc();//动态类型为 Derive *,虚函数动态绑定,参照动态类型,缺省参数一般静态绑定

      pbase = &base;//动态类型为 Base *
      pbase->virfunc();//
      /*
      Base myfunc
      ----------
      Derive virfunc val=2
      Derive virfunc val=1
      Base virfunc val=1

      */
    • 缺省参数一般静态绑定

    • 不要重新定义虚函数的缺省参数值

  • 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
        class A {
        public:
        virtual void myvirfunc()
        {
        cout <<"A myvirfunc" << endl;
        }
        };
        A*pa = new A();
        pa->myvirfunc();

        A a;
        a.myvirfunc();//非多态
        /*
        pa->myvirfunc();
        00572091 mov eax,dword ptr [pa]
        00572094 mov edx,dword ptr [eax]
        00572096 mov esi,esp
        00572098 mov ecx,dword ptr [pa]
        0057209B mov eax,dword ptr [edx]
        0057209D call eax
        0057209F cmp esi,esp
        005720A1 call __RTC_CheckEsp (0571267h)

        A a;
        005720A6 lea ecx,[a]
        005720A9 call A::A (0571672h)
        a.myvirfunc();//非多态
        005720AE lea ecx,[a]
        a.myvirfunc();//非多态
        005720B1 call A::myvirfunc (0571677h)
        */
    • 表现形式

      • 有继承关系,有父类,有子类,父类有虚函数,子类一定有虚函数,子类重写父类虚函数
      • 父类指针或引用指向子类对象
      • 当以父类指针或引用调用子类重写了的虚函数时

32 单继承虚函数回顾

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    class Base {
    public:
    virtual void f() { cout << "Base::f"<< endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    };

    class Derive :public Base {
    public:
    //virtual void i() { cout << "Derive::i" << endl; }
    virtual void g() { cout <<"Derive::g" << endl; }
    void myselffunc()
    {
    }
    };
  • 1563259723100

  • 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
    class Base {
    public:
    virtual void f() { cout << "Base::f"<< endl; }
    virtual void g() { cout << "Base::g" << endl; }
    virtual void h() { cout << "Base::h" << endl; }
    };

    class Derive :public Base {
    public:
    virtual void i() { cout << "Derive::i" << endl; }
    virtual void g() { cout <<"Derive::g" << endl; }
    void myselffunc()
    {
    }
    };
    /*
    pmyderive->h();
    00A72841 mov eax,dword ptr [pmyderive]
    00A72844 mov edx,dword ptr [eax]
    00A72846 mov esi,esp
    00A72848 mov ecx,dword ptr [pmyderive]
    00A7284B mov eax,dword ptr [edx+8]
    00A7284E call eax
    00A72850 cmp esi,esp
    00A72852 call __RTC_CheckEsp (0A7117Ch)
    pmyderive->i();
    00A72857 mov eax,dword ptr [pmyderive]
    00A7285A mov edx,dword ptr [eax]
    00A7285C mov esi,esp
    00A7285E mov ecx,dword ptr [pmyderive]
    00A72861 mov eax,dword ptr [edx+0Ch]
    00A72864 call eax
    00A72866 cmp esi,esp
    00A72868 call __RTC_CheckEsp (0A7117Ch)


    Base *pb = new Derive();
    pb->g();
    /*
    (*pb->vptr[1])(pb) //虚函数this指针 执行时确定的是哪个虚函数表,
    */
    */
  • 1566109393978

  • 子类中的虚函数表顺序和父类中虚函数表顺序一致,之后才是自己重新定义的虚函数表地址

  • 虚函数地址编译时已经确定,且在可执行文件中,运行时确定哪个虚函数表

  • vptr在编译期间产生,在构造函数中插入给vptr赋值代码,创建对象时,调用构造函数,vptr被赋值

  • 每个类对象有自己的虚函数表指针,有相同的虚函数表

  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Derive a1;
    Derive a2;

    Derive *pa3 = new Derive();

    /*
    0x00B3F718
    0x00B3F718 7c 9b 3b 00
    0x00B3F70C 7c 9b 3b 00
    0x00D3E0F8 7c 9b 3b 00
    */
  • 即便子类不重写任何虚函数,虚函数表指针同父类也不同

33 多继承虚函数深度解释、第二基类、虚析构必加

  • 多继承下的虚函数

    • 多重继承复杂性体现在第二个基类

    • 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
      class Base {
      public:
      virtual void f() { cout << "Base::f" << endl; }
      virtual void g() { cout << "Base::g" << endl; }
      virtual void h() { cout << "Base::h" << endl; }
      virtual void k() { cout << "Base::k" << endl; }
      };

      class Base2 {
      public:
      virtual void h2() { cout << "Base2::h2" << endl; }


      };

      class Derive :public Base,public Base2 {
      public:
      virtual void i() { cout << "Derive::i" << endl; }
      virtual void g() { cout << "Derive::g" << endl; }
      void myselffunc()
      {

      }
      };
      int main()
      {

      Base2* pb2 = new Derive();//pb2 地址为this调整过的地址
      /*
      Derive * temp=new Derive();
      Base2 *pb2=temp+sizeof(Base);

      Base2 *pb2=(Base2*) ((char*)temp+sizeof(Base));
      */

      delete pb2;// 报异常

      system("pause");
      }
  • 如何删除用第二基类指针new出来的继承类对象

    • 1
      Base2* pb2 = new Derive();//pb2 地址为this调整过的地址
    • 实际上要删除的是Derive对象

    • 确保Derive的析构函数被调用

    • 编译器调用静态类型的析构函数

    • 执行delete pb2时

      • 如果Base2中没有析构函数,编译器直接删除pb2开始的内存,异常,释放的内存不是new起始的内存

      • 如果Base2中存在析构函数,但非虚析构函数,delete pb2时,非虚析构函数被调用,直接删除pb2开始的内存,依然异常,析构函数不是虚函数,编译器实施静态绑定,delete pb2时,删除内存的开始地址即pb2的当前位置

      • 如果Base2中存在析构函数,为虚析构函数,delete pb2时正确释放内存

        • 在Derive的虚析构函数中调用Base2中虚析构函数

        • Base2存在虚析构函数,Derive没有虚析构函数,编译器生成Derive的虚析构函数

        • 只要Base2中析构函数为虚的,Derive中析构函数不管是否加virtual,都是虚函数

        • 1
          2
          3
          4
          5
          /*
          开发人员提示符
          dumpbin /all project100.obj > my.txt
          查看 虚析构函数
          */
    • 涉及继承所有类,不管子类,父类,虚析构函数,空也行

  • 为什么加虚析构函数

    • 当定义第二个基类指针指向子类时,删除此指针指向的内存。由于内存布局影响,此指针指向的内存为子类内存的一部分。删除时,无法完全删除。普通成员函数静态绑定,delete时,调用的是第二个基类的析构函数。而声明为虚析构函数后,由于虚函数动态绑定,调用析构函数时,会先调用子类的虚析构函数,之后调用基类的虚析构函数。

34 多继承第二基类虚函数支持、虚继承带虚函数

  • 多重继承第二基类对虚函数的影响

    • 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
      class Base {
      public:
      virtual void f() { cout << "Base::f" << endl; }
      virtual void g() { cout << "Base::g" << endl; }
      virtual void h() { cout << "Base::h" << endl; }
      virtual void k() { cout << "Base::k" << endl; }
      virtual Base * clone() const
      {
      return new Base();
      }
      };

      class Base2 {
      public:
      virtual void h2() { cout << "Base2::h2" << endl; }
      virtual ~Base2()
      {
      }

      virtual Base2 * clone() const
      {
      return new Base2();
      }
      };
      class Derive :public Base,public Base2 {
      public:
      virtual void i() { cout << "Derive::i" << endl; }
      virtual void g() { cout << "Derive::g" << endl; }
      void myselffunc()
      {
      }
      virtual Derive * clone() const
      {
      return new Derive();
      }
      };
    • 子类继承几个父类,子类就有几个虚函数表

    • 多重继承下,存在情况,第二个基类或者后续的基类对虚函数的支持产生影响,this指针调整

      • this指针目的让对象指针正确的指向对象首地址,从而正确的调用对象的成员函数或者说正确确定数据成员的存储位置

      • 通过指向第二个基类的指针调用子类的虚函数

        1
        2
        Base2 *pb2=new Derive();
        delete pb2;
      • 指向派生类的指针调用第二个基类中的虚函数

        1
        2
        Derive * pd2=new Derive();
        pd2->h2();
      • 允许虚函数的返回值类型有所变化

        1
        2
        3
        Base2* pb1 = new Derive();
        Base2* pb2 = pb1->clone();//执行Derive的克隆 Derive::clone()
        //pb1 调整指向Derive中,Base2地址,执行pb1->clone()时pb1调整到Derive对象的首地址,调用 Derive::clone()
  • 虚继承下的虚函数

    • 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
      class Base {
      public:
      virtual void f() { cout << "Base::f" << endl; }

      virtual ~Base()
      {
      }
      int m_basei;
      };

      class Derive :virtual public Base {
      public:

      virtual ~Derive()
      {
      }
      int m_derivei;
      };

      cout <<"sizeof(Derive): " <<sizeof(Derive)<< endl;//m_derivei+m_basei+vtbl+vbtbl

      Derive dobj;
      dobj.m_basei = 2;//13-16
      dobj.m_derivei = 5;//5-8

      Derive* pdobj = new Derive();//pdobj = 0x009f8d30 {m_derivei=0 }
      /*
      Base = { m_basei = 0 }
      __vfptr = 0x00899b50 {class35_multi_inherit_second_base_virtual_func.exe!const Derive::`vftable'} {0x0089108c {class35_multi_inherit_second_base_virtual_func.exe!Base::f(void)}, ...}
      [0] = 0x0089108c {class35_multi_inherit_second_base_virtual_func.exe!Base::f(void)}
      [1] = 0x0089145b {class35_multi_inherit_second_base_virtual_func.exe!Derive::`vector deleting destructor'(unsigned int)}
      m_basei = 0
      */
      pdobj->f();
      //虚函数表指针9-12
      //虚基类表指针1-5
      /*
      &dobj=0x006FFD64
      0x006FFD64 5c 9b 89 00 05 00 00 00 50 9b 89 00 02 00 00 00
      0x00899B50 8c 10 89 00 5b 14 89 00 00 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00

      */
    • 1566212440996

    • 1
      2
      3
      4
      5
      6
      7
      Derive *pderive = new Derive();//pderive = 0x012ee538 {m_derivei=0 }
      Base *pbase2 = (Base*)pderive;//pbase2 = 0x012ee540 {m_derivei=0 }
      pbase2->m_basei = 7;
      /*
      0x012EE538 5c 9b dc 00 00 00 00 00 50 9b dc 00 07 00 00 00
      0x012EE540 50 9b dc 00 07 00 00 00
      */
    • 繁杂,不深究!!!!!!!!!!!!!

      35 RTTI运行时类型识别与存储位置

    • RTTI

      • C++运行时类型识别,要求父类中至少有一个虚函数

      • RTTI能力靠typeid和dynamic_cast运算符体现

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        24
        Base* pb = new Derive();
        pb->g();

        Derive myderive;
        Base& yb = myderive;
        yb.g();

        cout << "typeid(*pb).name "<<typeid(*pb).name()<< endl;
        cout << "typeid(yb).name "<<typeid(yb).name() << endl;


        Derive *pderive = dynamic_cast<Derive*>(pb);
        if (pderive != nullptr)//成功转换,pderive为Derive对象
        {
        cout << "pb is Derive" << endl;
        pderive->myselffunc();
        }
        /*
        Derive::g
        Derive::g
        typeid(*pb).name class Derive
        typeid(yb).name class Derive
        pb is Derive
        */
    • RTTI实现原理

      • typeid返回的常量对象引用,常量对象类型一般为type_info(类)

      • 父类没有虚函数,typeid_name错误,显示基类

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        const std::type_info & tp = typeid(*pb); //type_info 不可拷贝构造,赋值
        Base *pb2 = new Derive();
        const std::type_info &tp2 = typeid(*pb2);

        if(tp==tp2)
        {
        cout << "all Derive"<< endl;
        }

        //静态类型,给什么 出什么
        cout <<"typeid(int).name() "<<typeid(int).name() << endl;
      • 1
        2
        3
        4
        5
        6
        //基类没有虚函数时
        Base * pb = new Derive();
        cout <<"typeid(pb).name() "<<typeid(pb).name() << endl;
        /*
        typeid(pb).name() class Base *
        */
      • 1566216183403
    • vptr,vtbl,rtti的type_info信息构造时机

      • rtti的info信息编译时完成

36 函数调用、继承关系性能

  • 函数调用中编译器的循环代码优化

    • 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
      namespace  ns1{
      __int64 mytest(int mv)
      {
      int i = 0;
      __int64 icount = 0;
      for (int i = 0; i < 1000000; i++)
      {
      icount += 1;
      }
      // 优化为 icount+=和值
      return icount;

      }
      void func()
      {
      clock_t start, end;
      start = clock();
      __int64 mycount = 0;
      for (int i = 0; i <= 1000; i++)
      {
      mycount += mytest(6);
      }
      // 优化为 mycount+=和值
      end = clock();
      cout << "cost times "<<end-start<< endl;
      cout << "mycount " << mycount << endl;
      }
      }
    • Dubug: 1546 Release: 1

    • 优化循环,把循环优化为1条语句

    • 在编译期间,编译器具有运算能力

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      void func()
      {
      clock_t start, end;
      start = clock();
      __int64 mycount = 0;
      for (int i = 0; i <= 1000; i++)
      {
      mycount += mytest(i);
      }
      // 优化为 mycount+=和值
      end = clock();
      cout << "cost times "<<end-start<< endl;
      cout << "mycount " << mycount << endl;
      }
    • Release 279ms

    • 给固定表达式,编译器将这种参数固定的函数调用,视为不变的表达式,尝试做优化

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      __int64 mytest(int mv)
      {
      int i = 0;
      __int64 icount = 0;
      for (int i = 0; i < 1000000; i++)
      {
      icount += 1;
      if ((i == 10000) && (mv == 999))
      {
      printf("\n");
      }

      }
      // 优化为 icount+=和值
      return icount;

      }
    • Release 633ms

  • 继承关系深度增加,开销一般也增加

    • 1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      namespace ns2 {
      class A {
      public: A()
      {
      cout <<"A::A()" << endl;
      }
      };
      class B :public A {
      public:
      };
      class C :public B {
      public:
      C()
      {
      cout << "C::C()" << endl;
      }
      };

      void func()
      {
      C cobj;

      }
      }
    • C构造调B构造,B构造调A构造

      • 当A有构造时,编译器会生成B构造,C调B,B调A 耗时增加
      • 当A没有构造时,不会生成B构造,耗时不大
    • 多重继承导致开销增加

      • 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
        namespace ns2 {
        class A {
        public: A()
        {
        cout <<"A::A()" << endl;
        }
        };
        class A1 {
        public: A1()
        {
        cout << "A1::A1()" << endl;
        }
        };
        class B :public A,public A1 {
        public:
        };
        class C :public B {
        public:
        C()
        {
        cout << "C::C()" << endl;
        }
        };

        void func()
        {
        C cobj;

        }
        }
  • 继承关系深度增加,虚函数导致的开销增加

    • 每多一次继承,在构造函数中增加 虚函数表指针 赋值的代码
    • 父类有虚函数,子类会生成默认构造函数

37 指向成员函数的指针及VCALL

  • 指向成员函数的指针

    • 成员函数地址,编译时已确定,调用成员函数,需要通过对象this指针实现

    • 所有常规非静态成员函数,都需要一个对象来调用

    • 成员函数第一个参数 this指针

    • 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
      namespace ns1 {

      class A {
      public:
      void myfunc1(int val1)
      {
      cout << " myfunc1 "<<val1<< endl;
      }
      void myfunc2(int val1)
      {
      cout << " myfunc2 " << val1 << endl;
      }
      static void mysfunc(int val1)
      {
      cout << " mysfunc " << val1 << endl;
      }
      };
      void func()
      {
      A obja;
      void (A::* p)(int val) = &A::myfunc1;//定义一个成员函数指针
      p = &A::myfunc2;

      (obja.*p)(15);// 通过成员函数指针调用成员函数,借用对象
      //p(&obja,15);
      A* pobj=new A();
      (pobj->*p)(20);//通过成员函数指针调用成员函数,借用对象指针
      //p(pobj,20);
      void(*ps)(int val1) = &A::mysfunc;// 普通函数指针
      ps(80);
      //通过成员函数指针对常规的成员函数调用的成本 ,和通过普通的函数指针调用静态成员函数,成本上差不多

      }
  • 指向虚成员函数的指针及VCALL

    • 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
      namespace ns2 {
      class A {
      public:
      void myfunc1(int val1)
      {
      cout << " myfunc1 " << val1 << endl;
      }
      void myfunc2(int val1)
      {
      cout << " myfunc2 " << val1 << endl;
      }
      static void mysfunc(int val1)
      {
      cout << " mysfunc " << val1 << endl;
      }
      virtual void myvirtualprev(int val1)
      {
      cout << " myvirtualprev " << val1 << endl;
      }
      virtual void myvirtual(int val1)
      {
      cout << " myvitual " << val1 << endl;
      }
      };
      void func()
      {
      void (A::* pvritual)(int val) = &A::myvirtual;
      A* pvobj = new A();
      pvobj->myvirtual(100);// 走虚函数表

      (pvobj->*pvritual)(200);//通过成员函数指针调用虚函数表,也走虚函数表

      printf(" %p\n",&A::myvirtual);//不是虚函数真正地址,为vcall[4]地址
      /*
      0040276A push offset ns2::A::`vcall'{4}' (04014A6h)
      0040276F push offset string " %p\n" (0409B70h)
      00402774 call _printf (04014BAh)
      00402779 add esp,8
      */
      delete pvobj;
      }
      }
    • vcall (vcall chunk)=virtual call 虚调用

    • 代表一段要执行的代码的地址,这段代码引导去执行正确的虚函数

    • 或者vcall虚函数表,vcall[0]即虚函数表里的第一个函数,vcall[4]第二个虚函数,一个指针4字节

    • &A::myvirtual 地址,地址中有一段代码,记录的是该虚函数在虚函数表中偏移值,有了偏移值,再加上具体的对象指针,就可以确定调用的是哪个虚函数表中的哪个虚函数

    成员函数指针,保存的可能是一个vcall地址(虚函数),要么可能是一个真正的函数地址

    vcall 引导编译器从虚函数表中找到实际的虚函数地址

  • vcall 在继承关系中的体现

    • 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
      namespace ns3 {

      class A {
      public:
      void myfunc1(int val1)
      {
      cout << " myfunc1 " << val1 << endl;
      }
      void myfunc2(int val1)
      {
      cout << " myfunc2 " << val1 << endl;
      }
      static void mysfunc(int val1)
      {
      cout << " mysfunc " << val1 << endl;
      }
      virtual void myvirtualprev(int val1)
      {
      cout << "A myvirtualprev " << val1 << endl;
      }
      virtual void myvirtual(int val1)
      {
      cout << " A myvitual " << val1 << endl;
      }
      virtual ~A() {}
      };
      class B :public A{
      public:

      virtual void myvirtual(int val1)
      {
      cout << " B myvitual " << val1 << endl;
      }
      virtual ~B() {}
      };

      void func()
      {
      B *p = new B();// 对象指针
      void (B::*pvirtual)(int val) = &A::myvirtual;
      //pvirtual 偏移量 ,父类和子类的 虚函数 偏移量都为0
      (p->*pvirtual)(120);//父类成员函数指针 最终调B的成员函数
      /*
      004B1EDE mov esi,esp
      004B1EE0 push 0Ah
      004B1EE2 mov ecx,dword ptr [p]
      004B1EE5 call dword ptr [pvirtual]
      004B1EE8 cmp esi,esp
      004B1EEA call __RTC_CheckEsp (04B11B3h)

      */

      printf(" &A::myvirtual %p \n", &A::myvirtual);// vcall 地址不一样
      //A::myvirtual = 0x008d3090 {class38_pointer_to_member_function.exe!ns3::A::myvirtual(int)}
      printf(" &B::myvirtual %p \n", &B::myvirtual);
      //B::myvirtual = 0x008d3130 {class38_pointer_to_member_function.exe!ns3::B::myvirtual(int)}
      }

      }
    • 对于虚成员函数的调用,对象决定调用哪个类的虚函数表,vcall决定了偏移,所以对于虚函数myvirfunc()不管是A中vcall还是B中vcall都是相同的偏移,而这里的成员函数指针,传入的对象是B,偏移是vcall{0},所以最终调用的是B的虚函数myvirfunc(),同时这里不管是赋值给A的myvirfunc()的vcall还是B的myvirfunc()的vcall,都是调用的是B的虚函数myvirfunc()

38 inline 函数

  • inline函数

    • 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
      namespace ns1 {
      int myfunc(int testv)
      {
      return testv*(5 + 4)*testv;
      }
      void myfunc()
      {
      int i = myfunc(12);
      cout <<"i "<<i << endl;
      }
      }
      /*
      int i = myfunc(12);
      000F24AE push 0Ch
      000F24B0 call ns1::myfunc (0F102Dh)
      000F24B5 add esp,4
      000F24B8 mov dword ptr [i],eax
      */
      //VS内联函数扩展 Ob1
      /*
      int i = myfunc(12);
      00511CFE mov eax,0Ch
      00511D03 imul ecx,eax,9
      00511D06 imul edx,ecx,0Ch
      00511D09 mov dword ptr [i],edx
      */
    • 优点,快,缺点代码膨胀

    • 编译器估计内联函数的复杂度,inline只是建议编译器

  • inline扩展

    • 形参被对应实参代替

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        int i = myfunc(12+15);
        cout <<"i "<<i << endl;
        /*
        int i = myfunc(12+15);
        00681CFE mov eax,1Bh
        00681D03 imul ecx,eax,9
        00681D06 imul edx,ecx,1Bh
        00681D09 mov dword ptr [i],edx
        */

        int a = 80;
        int i = myfunc(a + 15);// 先计算 和值 在替换形参
        /*
        int i = myfunc(a + 15);// 先计算 和值 在替换形参
        00382415 mov eax,dword ptr [a]
        00382418 add eax,0Fh
        0038241B mov dword ptr [ebp-20h],eax
        0038241E imul ecx,dword ptr [ebp-20h],9
        00382422 imul ecx,dword ptr [ebp-20h]
        00382426 mov dword ptr [i],ecx
        */
      • 编译器先求值,用实参替换形参

    • 局部变量的引入

      • 1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        19
        20
        21
        22
        23
        	inline int myfunc(int testv)
        {
        //return testv*(5 + 4)*testv;
        int temp;
        temp= testv*(5 + 4)*testv;
        return temp;
        }
        void func()
        {

        int i = myfunc(12+ 15);// 先计算 和值 在替换形参
        cout <<"i "<<i << endl;
        }

        /*
        int i = myfunc(12+ 15);// 先计算 和值 在替换形参
        004E240E mov eax,1Bh
        004E2413 imul ecx,eax,9
        004E2416 imul edx,ecx,1Bh
        004E2419 mov dword ptr [ebp-14h],edx
        004E241C mov eax,dword ptr [ebp-14h] //多了两条指令
        004E241F mov dword ptr [i],eax
        */
      • 局部变量尽量少用

    • inline失败

      • 简单的递归函数
文章目录
  1. 1. 25 两层结构时虚基类表内容分析
  2. 2. 26 三层结构时虚基类表内容分析
  3. 3. 27 成员变量地址、偏移、指针重申
  4. 4. 28 普通成员函数的调用方式
  5. 5. 29 虚函数、静态成员函数调用方式
  6. 6. 30 虚成员函数地址问题的vcall引入
  7. 7. 31 静态动态类型、绑定
  8. 8. 32 单继承虚函数回顾
  9. 9. 33 多继承虚函数深度解释、第二基类、虚析构必加
  10. 10. 34 多继承第二基类虚函数支持、虚继承带虚函数
  11. 11. 35 RTTI运行时类型识别与存储位置
  12. 12. 36 函数调用、继承关系性能
  13. 13. 37 指向成员函数的指针及VCALL
  14. 14. 38 inline 函数