父类指针、子类指针,多态,虚表

目录

父类指针指向子类对象

 子类指针指向父类对象

 多态

c++默认根据指针类型调用对应的函数

非多态demo

c++中的多态通过虚函数实现(virtual修饰)

虚函数实现demo

虚函数实现原理

汇编分析

 内存分析


父类指针指向子类对象

父类指针指向子类对象,是安全的的(继承方式必须是public)

 子类指针指向父类对象

子类指针指向父类对象是不安全的

 

struct Person
{
    int m_age;
};
struct Student : Person
{
    int m_score;
};

int main()
{
    // 父类指针指向子类对象,是安全的的(继承方式必须是public)
    Person *p = new Student();
    p->m_age = 10;

    //子类指针指向父类对象是不安全的
   /*
    Student *p =(Student * ) new Person();
    p->m_age = 10;
    p->m_score = 100; // 报错
    */
    return 0;
}

 

 多态

c++默认根据指针类型调用对应的函数

非多态demo

#无多态,c++默认根据指针类型调用对应的函数。
#include <iostream>
using namespace std;
struct Animal
{
    void speak(){
        cout << "Animal::speak()" << endl;
    }
    void run(){
        cout << "Animal::run()" << endl;
    }
};
struct Dog : Animal
{
    //重写(复写,覆盖,override),重写要求与父类完全一样的函数
    void speak(){
        cout << "Dog::speak()" << endl;
    }
    //重载,
    void speak(int age){

        cout << "Dog::speak age :" << age << endl;
    }
    void run(){
        cout << "Dog::run()" << endl;
    }
};
struct Cat : Animal
{
    void speak(){
        cout << "Cat::speak()" << endl;
    }
    void run(){
        cout << "Cat::run()" << endl;
    }
};


void liu(Animal *p){
    p->speak();
    p->run();
}
int main()
{
    liu(new Dog());//指针是Animal *p,默认调用Animal的函数
    liu(new Cat());
    return 0;
}

输出:
Animal::speak()
Animal::run()
Animal::speak()
Animal::run()

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果

目的:传入的对象不同,实现的结果不同

c++中的多态通过虚函数实现(virtual修饰)

父类的函数是虚函数,子类重写的函数也是虚函数。

虚函数实现demo

#include <iostream>
using namespace std;
struct Animal
{
    virtual void speak(){
        cout << "Animal::speak()" << endl;
    }
    virtual void run(){
        cout << "Animal::run()" << endl;
    }
};
struct Dog : Animal
{
    //重写(复写,覆盖,override),重写要求与父类完全一样的函数
    void speak(){
        cout << "Dog::speak()" << endl;
    }
    //重载,
    void speak(int age){

        cout << "Dog::speak age :" << age << endl;
    }
    void run(){
        cout << "Dog::run()" << endl;
    }
};
struct Cat : Animal
{
    void speak(){
        cout << "Cat::speak()" << endl;
    }
    void run(){
        cout << "Cat::run()" << endl;
    }
};


void liu(Animal *p){
    p->speak();
    p->run();
}
int main()
{
    liu(new Dog()); //父类指针调用子类对象,利用父类指针调用(重写的)成员函数
    liu(new Cat());
    return 0;
}

结果:
Dog::speak()
Dog::run()
Cat::speak()
Cat::run()

虚函数实现原理

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

Animal *cat = new Cat(); //初始化成员变量为0
Animal *cat = new Cat;//不初始化成员变量

汇编分析

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;
    }
    void run(){
        cout << "Cat::run()" << endl;
    }
};

int main()
{
    Animal *cat = new Cat();
    cat->m_age = 20;
    cat->speak();
    cat->run();
    return 0;
}

// 调用speak 汇编分析
 Animal *cat = new Cat();
 cat->m_age = 20;
 cat->speak();

//ebp-8是指针变量cat的地址
mov eax,dword ptr [ebp-8] //找到指针变量cat的地址,取出cat存贮的地址给eax,eax是Cat对象的地址值
mov edx,dword ptr [eax] //找到Cat对象的地址,取出前面4个字节(虚表的地址)给edx
mov eax,dword ptr [edx] // 根据Cat对象前面的四个字节存储数据(地址值:0x00B89B64,是虚表的地址),找到存储空间,取出虚表前面四个字节存储的数据(地址值:0x00B814E7)给eax
call eax //调用eax(0x00B814E7),即Cat:speak的调用地址


// 调用run 汇编分析
 cat->run();

//ebp-8是指针变量cat的地址
mov eax,dword ptr [ebp-8] //找到指针变量cat的地址,取出cat存贮的地址给eax,eax是Cat对象的地址值
mov edx,dword ptr [eax] //找到Cat对象的地址,取出前面4个字节(虚表的地址)给edx
mov eax,dword ptr [edx+4] // 根据Cat对象前面的四个字节存储数据(地址值:0x00B89B64,是虚表的地址),找到存储空间,跳过虚表的4个字节后取出4个字节存储的数据(地址值:0x00B814CE)给eax
call eax //调用eax(0x00B814CE),即Cat:run的调用地址

 内存分析

虚表(X86环境图) 

内存地址  内存数据 内存地址内存数据
cat0x00E69B600x00B89B64虚表0x00B89B64

0x00B814E7

0x00E69B610x00B89B65
0x00E69B620x00B89B66
0x00E69B630x00B89B67
&m_age0x00E69B64200x00B89B680x00B814CE
0x00E69B650x00B89B69
0x00E69B660x00B89B6A
0x00E69B670x00B89B6B
&m_life0x00E69B680
0x00E69B69
0x00E69B6ACat:speak的调用地址:0x00B814E7
0x00E69B6BCat:run的调用地址:0x00B814CE

cat指针指向的是cat对象存储空间,然后可找到12个字节,首先可以取出前面四个字节是虚表地址,然后根据地址找到虚表。

E8开头的机器指令是 直接call 地址 

FF 开头的call,是从寄存器(rec)call。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值