美文网首页
09-虚表、抽象类和多继承

09-虚表、抽象类和多继承

作者: 一亩三分甜 | 来源:发表于2021-07-23 10:24 被阅读0次

    《C++文章汇总》
    上一篇介绍了引用和汇编《08-初始化列表、父类构造函数、虚函数和多态》,本文介绍虚表、抽象类和多继承。

    1.虚表

    ◼ 虚函数的实现原理是虚表,这个虚表里面存储着最终需要调用的虚函数地址,这个虚表也叫虚函数表

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
    };
    struct Dog:Animal{
        void speak(){
            cout << "Dog::speak()" << endl;
        }
        void run(){
            cout << "Dog::run()" << endl;
        }
    };
    struct Cat:Animal{
        int m_life;
        void speak(){
            cout << "Cat::speak()" << endl;
        }
        void run(){
            cout << "Cat::run()" << endl;
        }
    };
    struct Pig:Animal{
        void speak(){
            cout << "Pig::speak()" << endl;
        }
        void run(){
            cout << "Pig::run()" << endl;
        }
    };
    void liu(Animal *p){
        p->speak();
        p->run();
    }
    int main(){
        Animal *cat = new Cat();
        cat->m_age = 20;
        cat->speak();
        cat->run();
        
        cat = new Pig();
        cat->speak();
        cat->run();
        return 0;
    }
    //输出
    Cat::speak()
    Cat::run()
    Pig::speak()
    Pig::run()
    

    x86环境的虚表图


    image

    虚表汇编分析

    //调用speak
    Animal *cat = new Cat();
    cat->speak();
    //ebp-8是指针变量cat的地址
    //eax存储的是cat对象的地址
    mov eax,dword ptr [ebp-8]
    //取出Cat对象的最前面的4个字节(虚表地址)给edx
    //取出虚表的地址值给edx
    mov edx,dword ptr [eax]
    //取出虚表的最前面的4个字节给eax
    //取出Cat::speak的函数调用地址给eax
    mov eax,dword ptr [edx]
    
    //call Cat::speak
    call eax
    
    //调用run
    Animal *cat = new Cat();
    cat->run();
    //ebp-8是指针变量cat的地址
    //eax存储的是Cat对象的地址
    mov eax,dword ptr [ebp-8]
    
    //取出Cat对象的最前面的4个字节(虚表地址)给edx
    mov edx,dword ptr [eax]
    
    //跳过虚表的最前面4个字节,取出4个字节赋值给eax
    //取出Cat::run的函数调用地址给eax
    mov eax,dword ptr [edx+4]
    //call Cat::run
    call eax
    

    虚表内存分析

    //Cat对象的地址
    0x016DDB98
    //虚表地址
    0x00259b34
    //虚表的内容
    0x00251569
    0x00251550
    

    ◼ 所有的Cat对象(不管在全局区、栈、堆)共用同一份虚表
    子类中没有方法时,会调用父类中virtual修饰的方法,放入子类对象(cat对象)的前4个字节内存地址中

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
    };
    struct Dog:Animal{
        void speak(){
            cout << "Dog::speak()" << endl;
        }
        void run(){
            cout << "Dog::run()" << endl;
        }
    };
    struct Cat:Animal{
        int m_life;
    //    void speak(){
    //        cout << "Cat::speak()" << endl;
    //    }
        void run(){
            cout << "Cat::run()" << endl;
        }
    };
    struct Pig:Animal{
    //    void speak(){
    //        cout << "Pig::speak()" << endl;
    //    }
        void run(){
            cout << "Pig::run()" << endl;
        }
    };
    void liu(Animal *p){
        p->speak();
        p->run();
    }
    int main(){
        Animal *cat = new Cat();
        cat->m_age = 20;
        cat->speak();
        cat->run();
        
        cat = new Pig();
        cat->speak();
        cat->run();
        return 0;
    }
    //输出
    Animal::speak()
    Cat::run()
    Animal::speak()
    Pig::run()
    
    图片.png

    父类没有子类继承,有virtual修饰的方法函数,则也存在虚表,调用时会从虚表中查找调用函数的地址

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
    };
    int main(){
        Animal *ani = new Animal();
        ani->m_age = 20;
        ani->speak();
        ani->run();
    }
    

    子类中不存在virtual修饰的函数,仍存在虚表

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
    };
    struct Cat:Animal{
        int m_life;
        void speak(){
            cout << "Cat::speak()" << endl;
        }
    };
    struct WhiteCat:Cat{
        
    };
    int main(){
        Animal *ani = new WhiteCat();
        ani->speak();
        ani->run();
        
        return 0;
    }
    //输出
    Cat::speak()
    Animal::run()
    

    若虚函数写在子类中,则从子类开始有虚表

    struct Animal{
        int m_age;
        void speak(){
            cout << "Animal::speak()" << endl;
        }
        void run(){
            cout << "Animal::run()" << endl;
        }
    };
    struct Cat:Animal{
        int m_life;
        virtual void speak(){
            cout << "Cat::speak()" << endl;
        }
        virtual void run(){
            cout << "Cat::run()" << endl;
        }
    };
    struct WhiteCat:Cat{
        void speak(){
            cout << "WhiteCat::speak()" << endl;
        }
        void run(){
            cout << "WhiteCat::run()" << endl;
        }
    };
    int main(){
        Animal *ani = new WhiteCat();
        ani->speak();
        ani->run();
        
        Cat *cat = new WhiteCat();
        cat->speak();
        cat->run();
        return 0;
    }
    //输出
    Animal::speak()
    Animal::run()
    WhiteCat::speak()
    WhiteCat::run()
    

    子类的函数是虚函数,要么是从父类重写,要么是自己在函数名前加virtual

    2.调用父类的成员函数

    先执行父类的重写的虚函数,C++没有super关键字,用父类的类名调用

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
    };
    struct Cat:Animal{
        int m_life;
        void speak(){
            Animal::speak();
            cout << "Cat::speak()" << endl;
        }
        void run(){
            cout << "Cat::run()" << endl;
        }
    };
    int main(){
        Animal *ani = new Cat();
        ani->speak();
        ani->run();
        return 0;
    }
    //输出
    Animal::speak()
    Cat::speak()
    Cat::run()
    

    3.虚析构函数

    ◼ 如果存在父类指针指向子类对象的情况,应该将析构函数声明为虚函数(虚析构函数)
    delete父类指针时,才会调用子类的析构函数,保证析构的完整性,先调用子类析构函数后调用父类析构函数

    struct Animal{
        int m_age;
        virtual void speak(){
            cout << "Animal::speak()" << endl;
        }
        virtual void run(){
            cout << "Animal::run()" << endl;
        }
        Animal(){
            cout << "Animal::Animal()" << endl;
        }
        virtual ~Animal(){
            cout << "Animal::~Animal()" << endl;
        }
    };
    
    struct Cat:Animal{
        int m_life;
        void speak(){
            cout << "Cat::speak()" << endl;
        }
        void run(){
            cout << "Cat::run()" << endl;
        }
        Cat(){
            cout << "Cat::Cat()" << endl;
        }
        ~Cat(){
            cout << "Cat::~Cat()" << endl;
        }
    };
    int main(){
        Animal *ani = new Cat();
        delete ani;
        return 0;
    }
    //输出
    Animal::Animal()
    Cat::Cat()
    Cat::~Cat()
    Animal::~Animal()
    

    4.纯虚函数

    ◼ 纯虚函数:没有函数体且初始化为0的虚函数,用来定义接口规范

    struct Animal{
        virtual void speak() = 0;
        virtual void run() = 0;
    };
    struct Dog:Animal{
        void speak(){
            cout << "Dog::speak()" << endl;
        }
        void run(){
            cout << "Dog::run()" << endl;
        }
    };
    struct Cat:Animal{
        void speak(){
            Animal::speak();
            cout << "Cat::speak()" << endl;
        }
        void run(){
            cout << "Cat::run()" << endl;
        }
    };
    struct Pig:Animal{
        void speak(){
            cout << "Pig::speak()" << endl;
        }
        void run(){
            cout << "Pig::run()" << endl;
        }
    };
    int main(){
        Animal *ani = new Pig();
        ani->speak();
        ani->run();
        
        getchar();
        return 0;
    }
    //输出
    Pig::speak()
    Pig::run()
    

    5.抽象类(Abstract Class)

    含有纯虚函数的类,不可以实例化(不可以创建对象)
    抽象类也可以包含非纯虚函数、成员变量

    struct Animal{
        int m_age;
        virtual void speak() = 0;
        virtual void run() = 0;
        void eat(){}
    };
    

    如果父类是抽象类,子类没有完全重写纯虚函数,那么这个子类依然是抽象类

    //抽象类
    struct Animal{
        int m_age;
        virtual void speak() = 0;
        virtual void run() = 0;
        void eat(){
            
        }
    };
    //抽象类
    struct Dog:Animal{
        void speak(){
            cout << "Dog::speak()" << endl;
        }
    };
    //非抽象类
    struct HaShiQi:Dog{
        void speak(){
            cout << "HaShiQi::speak()" << endl;
        }
        void run(){
            cout << "HaShiQi::run()" << endl;
        }
    };
    

    6.多继承

    ◼ C++允许一个类可以有多个父类(不建议使用,会增加程序设计复杂度)


    image
    struct Student{
        int m_score;
        void study(){
            cout << "Student::study() - score = " << m_score << endl;
        }
    };
    struct Worker{
        int m_salary;
        void work(){
            cout << "Worker::work() - salary = " << m_salary << endl;
        }
    };
    struct Undergraduate:Student,Worker{
        int m_grade;
        void play(){
            cout << "Undergraduate::play() - m_grade = " << m_grade << endl;
        }
    };
    int main(){
        Undergraduate *u = new Undergraduate();
        u->m_score = 100;
        u->m_salary = 2000;
        u->m_grade = 4;
        u->work();
        u->study();
        u->play();
        
        return 0;
    }
    //输出
    Worker::work() - salary = 2000
    Student::study() - score = 100
    Undergraduate::play() - m_grade = 4
    

    7.多继承体系下的构造函数调用

    struct Student{
        int m_score;
        Student(int score):m_score(score){}
        void study(){
            cout << "Student::study() - score = " << m_score << endl;
        }
    };
    struct Worker{
        int m_salary;
        Worker(int salary):m_salary(salary){}
        void work(){
            cout << "Worker::work() - salary = " << m_salary << endl;
        }
    };
    struct Undergraduate:Student,Worker{
        int m_grade;
        Undergraduate(int grade,int score,int salary):m_grade(grade),Student(score),Worker(salary){}
        void play(){
            cout << "Undergraduate::play() - m_grade = " << m_grade << endl;
        }
    };
    int main(){
        Undergraduate *u = new Undergraduate(4,100,2000);
        cout << u->m_grade << endl;
        cout << u->m_score << endl;
        cout << u->m_salary << endl;
        return 0;
    }
    //输出
    4
    100
    2000
    

    8.多继承-虚函数

    ◼ 如果子类继承的多个父类都有虚函数,那么子类对象就会产生对应的多张虚表


    图片.png

    9.同名函数

    struct Student{
        int m_score;
        void eat(){
            cout << "Student::eat()" << endl;
        }
    };
    struct Worker{
        int m_salary;
        void eat(){
            cout << "Worker::eat()" << endl;
        }
    };
    struct Undergraduate:Student,Worker{
        int m_grade;
        void eat(){
            cout << "Undergraduate::eat()" << endl;
        }
    };
    int main(){
        Undergraduate ug;
        ug.eat();
        ug.Student::eat();
        ug.Worker::eat();
        ug.Undergraduate::eat();
        
        return 0;
    }
    //输出
    Undergraduate::eat()
    Student::eat()
    Worker::eat()
    Undergraduate::eat()
    

    若子类没有eat(),直接调用eat()会产生二义性报错

    10.同名成员变量

    struct Student{
        int m_age;
    
    };
    struct Worker{
        int m_age;
    };
    struct Undergraduate:Student,Worker{
        int m_age;
    };
    int main(){
        cout << sizeof(Undergraduate) << endl;
        Undergraduate ug;
        ug.m_age = 10;
        cout << ug.m_age << endl;
        ug.Student::m_age = 20;
        ug.Worker::m_age = 23;
        cout << ug.Student::m_age << endl;
        cout << ug.Worker::m_age << endl;
        
        return 0;
    }
    //输出
    12
    10
    20
    23
    
    image

    相关文章

      网友评论

          本文标题:09-虚表、抽象类和多继承

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