美文网首页
多态--虚函数

多态--虚函数

作者: const_qiu | 来源:发表于2020-08-25 21:31 被阅读0次

1.通过对象调用成员函数(无虚函数)

  • 源代码

class Base {
public:
    void Function_1() {
        printf("func1...\n");
    }
    void Function_2() {
        printf("func2...\n");
    }

};


int main()
{
    Base  base;
    base.Function_1();
    base.Function_2();

}

  • 反汇编代码
    Base  base;
    base.Function_1();
00D419E2 8D 4D F7             lea         ecx,[base]  
00D419E5 E8 45 F9 FF FF       call        Base::Function_1 (0D4132Fh)  
    base.Function_2();
00D419EA 8D 4D F7             lea         ecx,[base]  ;ecx = this指针,对象首地址
00D419ED E8 5C F8 FF FF       call        Base::Function_2 (0D4124Eh) ; E8 call 直接调用

2. 通过对象调用成员函数(有虚函数)

class Base {
public:
    void Function_1() {
        printf("func1...\n");
    }
    virtual void Function_2() {
        printf("func2...\n");
    }

};


int main()
{
    Base  base;
    base.Function_1();
    base.Function_2();

}

    Base  base;
009C1A82 8D 4D F4             lea         ecx,[base]  
009C1A85 E8 C4 F7 FF FF       call        Base::Base (09C124Eh)  
    base.Function_1();
009C1A8A 8D 4D F4             lea         ecx,[base]  ;
009C1A8D E8 C0 F8 FF FF       call        Base::Function_1 (09C1352h)  
    base.Function_2();
009C1A92 8D 4D F4             lea         ecx,[base]  ;;
009C1A95 E8 3D F6 FF FF       call        Base::Function_2 (09C10D7h)  ;
  • 对比发现通过对象调用普通函数与虚函数的汇编代码没有区别,都是E8 call(直接调用)

3. 通过对象指针调用成员函数(有虚函数)

类中有虚函数时(无继承),类的大小会增加4字节,存放的是一个指向虚表的指针,虚表指针存放在对象首地址

class Base {
public:
    void Function_1() {
        printf("func1...\n");
    }
    virtual void Function_2() {
        printf("func2...\n");
    }

};


int main()
{
    Base  base;
    Base* pb = &base;
    pb->Function_1(); 
    pb->Function_2(); 

}

  Base  base;
00BF1A82 8D 4D F4             lea         ecx,[base]  
00BF1A85 E8 C4 F7 FF FF       call        Base::Base (0BF124Eh)  
    Base* pb = &base;
00BF1A8A 8D 45 F4             lea         eax,[base]  
    Base* pb = &base;
00BF1A8D 89 45 E8             mov         dword ptr [pb],eax  
    pb->Function_1(); 
00BF1A90 8B 4D E8             mov         ecx,dword ptr [pb]  
00BF1A93 E8 BA F8 FF FF       call        Base::Function_1 (0BF1352h)  ;普通函数还是直接调用
    pb->Function_2(); 
00BF1A98 8B 45 E8             mov         eax,dword ptr [pb]  
00BF1A9B 8B 10                mov         edx,dword ptr [eax]  
00BF1A9D 8B F4                mov         esi,esp  
00BF1A9F 8B 4D E8             mov         ecx,dword ptr [pb]  
00BF1AA2 8B 02                mov         eax,dword ptr [edx]  ;
00BF1AA4 FF D0                call        eax  ; FF call 是间接调用,eax存的是某个虚函数地址

4. 通过虚表指针调用虚函数

class Base {
public:
    Base() {
        int  x = 1;
        int  y = 1;

    }
    int x;
    int  y;
    void Function_1() {
        printf("func1...\n");
    }
    virtual void Function_2() {
        printf("func2...\n");
    }
    virtual void Function_3() {
        printf("func3...\n");
    }
    virtual void Function_4() {
        printf("func4...\n");
    }
    virtual void Function_5() {
        printf("func5...\n");
    }
};

//定义一个函数指针类型

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;

    Base  base;
    Base* pb = &base;
    //对象的前四个字节为虚表指针
    //printf("base的虚表地址为:%d\n",*(int*)&base);
    //通过虚表指针调用虚函数
    
    for (int i = 0; i < 4; i++) {
        int tmp = *((int*)(*(int*)&base) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    /*pb->Function_1(); 
    pb->Function_2(); */

}

5 有继承关系打印虚表

5.1 单继承无函数重写

struct Base {
public:
    Base() {
        int  x = 1;
        int  y = 1;

    }
    int x;
    int  y;
    virtual void Function_1() {
        printf("base:func1...\n");
    }
    virtual void Function_2() {
        printf("base:func2...\n");
    }
    virtual void Function_3() {
        printf("base:func3...\n");
    }
    
};

struct Sub :Base {


    virtual void Function_4() {
        printf("Sub:func4...\n");
    }
    virtual void Function_5() {
        printf("Sub:func5...\n");
    }
    virtual void Function_6() {
        printf("Sub:func6...\n");
    }


};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;

    Sub  sub;
    Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    //printf("base的虚表地址为:%d\n",*(int*)&base);
    //通过虚表指针调用虚函数
    
    for (int i = 0; i < 6; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    /*pb->Function_1(); 
    pb->Function_2(); */

}
  • 运行结果:
base:func1...
base:func2...
base:func3...
Sub:func4...
Sub:func5...
Sub:func6...

5.2 单继承有函数重写

struct Base {
public:
    Base() {
        int  x = 1;
        int  y = 1;

    }
    int x;
    int  y;
    virtual void Function_1() {
        printf("base:func1...\n");
    }
    virtual void Function_2() {
        printf("base:func2...\n");
    }
    virtual void Function_3() {
        printf("base:func3...\n");
    }
    
};

struct Sub :Base {


    virtual void Function_1() {
        printf("Sub:func1...\n");
    }
    virtual void Function_2() {
        printf("Sub:func2...\n");
    }
    virtual void Function_6() {
        printf("Sub:func6...\n");
    }

};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;
    Sub  sub;
    Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    //printf("base的虚表地址为:%d\n",*(int*)&base);
    //通过虚表指针调用虚函数
    
    for (int i = 0; i < 4; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    /*pb->Function_1(); 
    pb->Function_2(); */

}
  • 运行结果: 子类覆盖父类中同名的
Sub:func1...
Sub:func2...
base:func3...
Sub:func6...

5.3 多继承无函数重写

多继承,假如有两个直接父类,会有两张虚表,对象的前八个字节为两个虚表指针

#include <iostream>
#include <stdio.h>
using namespace std;
struct Base1 {

    virtual void Function_1() {
        printf("base1:func1...\n");
    }
    virtual void Function_2() {
        printf("base1:func2...\n");
    }
    virtual void Function_3() {
        printf("base1:func3...\n");
    }
    
};
struct Base2 {

    virtual void Function_4() {
        printf("base2:func4...\n");
    }
    virtual void Function_5() {
        printf("base2:func5...\n");
    }
    virtual void Function_6() {
        printf("base2:func6...\n");
    }

};

struct Sub :Base1,Base2 {
    int x = 5;
    int y = 6;

    virtual void Function_7() {
        printf("Sub:func7...\n");
    }
    virtual void Function_8() {
        printf("Sub:func8...\n");
    }
    virtual void Function_9() {
        printf("Sub:func9...\n");
    }

};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;
    Sub  sub;

    //Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    
    //通过虚表指针调用虚函数
    //打印第一张虚表
    printf("打印第一张虚表\n");
    printf("第一张虚表地址为:%ld\n", *(int*)&sub);
    for (int i = 0; i < 6; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    printf("打印第二张虚表\n");
    printf("第二张虚表地址为:%ld\n", *(int*)((int)&sub+4));
    //打印第二张虚表
    pFunction pFn2;
    for (int i = 0; i < 3; i++) {
        int tmp = *((int*)(*(int*)((int)&sub + 4)) + i);
        pFn2 = (pFunction)tmp;
        pFn2();

    }

}
  • 运行结果
打印第一张虚表
第一张虚表地址为:1690464
base1:func1...
base1:func2...
base1:func3...
Sub:func7...
Sub:func8...
Sub:func9...
打印第二张虚表
第二张虚表地址为:1691084
base2:func4...
base2:func5...
base2:func6...

5.4 多继承有函数重写

#include <iostream>
#include <stdio.h>
using namespace std;
struct Base1 {

    virtual void Function_1() {
        printf("base1:func1...\n");
    }
    virtual void Function_2() {
        printf("base1:func2...\n");
    }
    virtual void Function_3() {
        printf("base1:func3...\n");
    }
    
};
struct Base2 {

    virtual void Function_4() {
        printf("base2:func4...\n");
    }
    virtual void Function_5() {
        printf("base2:func5...\n");
    }
    virtual void Function_6() {
        printf("base2:func6...\n");
    }

};

struct Sub :Base1,Base2 {
    int x = 5;
    int y = 6;

    virtual void Function_1() {
        printf("Sub:func1...\n");
    }
    virtual void Function_4() {
        printf("Sub:func4...\n");
    }
    virtual void Function_7() {
        printf("Sub:func7...\n");
    }

};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;
    Sub  sub;

    //Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    
    //通过虚表指针调用虚函数
    //打印第一张虚表
    printf("打印第一张虚表\n");
    printf("第一张虚表地址为:%ld\n", *(int*)&sub);
    for (int i = 0; i < 4; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    printf("打印第二张虚表\n");
    printf("第二张虚表地址为:%ld\n", *(int*)((int)&sub+4));
    //打印第二张虚表
    pFunction pFn2;
    for (int i = 0; i < 3; i++) {
        int tmp = *((int*)(*(int*)((int)&sub + 4)) + i);
        pFn2 = (pFunction)tmp;
        pFn2();

    }

}
  • 运行结果

重写的是哪个就在哪个表里

打印第一张虚表
第一张虚表地址为:13028192
Sub:func1...
base1:func2...
base1:func3...
Sub:func7...
打印第二张虚表
第二张虚表地址为:13028812
Sub:func4...
base2:func5...
base2:func6...

5.5 多层继承无函数重写

多层继承只有一个虚表

#include <iostream>
#include <stdio.h>
using namespace std;
struct Base1 {

    virtual void Function_1() {
        printf("base1:func1...\n");
    }
    virtual void Function_2() {
        printf("base1:func2...\n");
    }
    
    
};
struct Base2:Base1 {

    virtual void Function_3() {
        printf("base2:func4...\n");
    }
    virtual void Function_4() {
        printf("base2:func5...\n");
    }
    
};

struct Sub :Base2 {
    
    virtual void Function_5() {
        printf("Sub:func5...\n");
    }
    virtual void Function_6() {
        printf("Sub:func6...\n");
    }


};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;
    Sub  sub;

    //Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    
    //通过虚表指针调用虚函数
    //打印第一张虚表
    printf("打印第一张虚表\n");
    printf("第一张虚表地址为:%ld\n", *(int*)&sub);
    for (int i = 0; i < 6; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    //printf("打印第二张虚表\n");
    //printf("第二张虚表地址为:%ld\n", *(int*)((int)&sub+4));
    ////打印第二张虚表
    //pFunction pFn2;
    //for (int i = 0; i < 3; i++) {
    //  int tmp = *((int*)(*(int*)((int)&sub + 4)) + i);
    //  pFn2 = (pFunction)tmp;
    //  pFn2();

    //}

}
  • 运行结果:
打印第一张虚表
第一张虚表地址为:4967372
base1:func1...
base1:func2...
base2:func4...
base2:func5...
Sub:func5...
Sub:func6...

5.5 多层继承有函数重写

#include <iostream>
#include <stdio.h>
using namespace std;
struct Base1 {

    virtual void Function_1() {
        printf("base1:func1...\n");
    }
    virtual void Function_2() {
        printf("base1:func2...\n");
    }
    
    
};
struct Base2:Base1 {

    virtual void Function_3() {
        printf("base2:func3...\n");
    }
    virtual void Function_4() {
        printf("base2:func4...\n");
    }
    
};

struct Sub :Base2 {
    
    virtual void Function_1() {
        printf("Sub:func1...\n");
    }
    virtual void Function_3() {
        printf("Sub:func3...\n");
    }


};

//定义一个函数指针

typedef void(*pFunction)(void);
int main()
{   
    pFunction pFn;
    Sub  sub;

    //Base* pb = &sub;//父类指针指向子类对象
    //对象的前四个字节为虚表指针
    
    //通过虚表指针调用虚函数
    //打印第一张虚表
    printf("打印第一张虚表\n");
    printf("第一张虚表地址为:%ld\n", *(int*)&sub);
    for (int i = 0; i < 4; i++) {
        int tmp = *((int*)(*(int*)&sub) + i);
        pFn = (pFunction)tmp;
        pFn();

    }
    //printf("打印第二张虚表\n");
    //printf("第二张虚表地址为:%ld\n", *(int*)((int)&sub+4));
    ////打印第二张虚表
    //pFunction pFn2;
    //for (int i = 0; i < 3; i++) {
    //  int tmp = *((int*)(*(int*)((int)&sub + 4)) + i);
    //  pFn2 = (pFunction)tmp;
    //  pFn2();

    //}

}
  • 运行结果:
打印第一张虚表
第一张虚表地址为:6015948
Sub:func1...
base1:func2...
Sub:func3...
base2:func4...

相关文章

  • 查漏补缺

    C++虚函数: 多态: 静态多态(重载)、动态多态(虚函数) 虚函数 虚函数表:编译器为每个类创建了一个虚函数表...

  • C++虚函数注意事项以及构成多态的条件

    C++ 虚函数对于多态具有决定性的作用,有虚函数才能构成多态。 虚函数注意事项 只需要在虚函数的声明处加上 vir...

  • 深刻剖析之c++博客文章

    三大特性 封装、继承、多态 多态 C++ 虚函数表解析C++多态的实现原理 介绍了类的多态(虚函数和动态/迟绑定)...

  • 多态与虚函数

    多态与虚函数 注意 在成员函数(静态成员、构造函数和析构函数除外)中调用同类的虚函数的语句是多态的。 在构造函数和...

  • 多态 虚函数

  • 多态--虚函数

    1.通过对象调用成员函数(无虚函数) 源代码 反汇编代码 2. 通过对象调用成员函数(有虚函数) 对比发现通过对象...

  • 多态

    virtual 关键字定义虚函数多态是通过指向虚函数表的虚指针实现的。 例子

  • 多态、虚函数、虚函数表

    多态 默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态 多态是面向对象非常重要的一个特性同一操作作用于...

  • 语法

    virtual 1.virtual声明的函数实现多态就是通用的多态实现 2.纯虚函数C++的纯虚函数用于表示一个类...

  • 第十五章 多态性和虚函数

    被virtual关键字修饰的成员函数就是虚函数。虚函数的作用是实现多态性。所谓多态性就是以共同的方法,对不同的对象...

网友评论

      本文标题:多态--虚函数

      本文链接:https://www.haomeiwen.com/subject/rulzjktx.html