c++多态实现(二)虚函数表构造和重写行为

4.7 多态
4.7.1 多态的基本概念
多态是C++面向对象三大特性之一
多态分为两类

  • 静态多态:函数重载和 运算符重载属于静态多态,复用函数名
  • 动态多态: 派生类和虚函数实现运行时多态静态多态和动态多态区别:

静态多态的函数地址早绑定 - 编译阶段确定函数地址
动态多态的函数地址晚绑定 - 运行阶段确定函数地址
来源:黑马程序员54 类和对象-多态-多态的原理剖析_哔哩哔哩_bilibili

多态的实现原理:

当有虚函数的时候,类内会有一个虚函数(表)指针vfptr,这个指针指向这个类的虚函数表,虚函数表记录了各个虚函数的入口地址,当发生继承的时候,子类也会有一张一样的但是地址不同的表 

下面我们通过实验验证我们的猜想:

class Animal{
public:
     virtual void speak(){
        cout<<"woow"<<endl;
     }
     virtual void speak_1(){
        cout<<"bulk!"<<endl;
     }
     void give_ptr(){
        void (Animal::*p)();
        p=speak;
        cout<<p<<endl;
     }
};
class Cat:public Animal{

    void speak(){
        cout<<"Miao"<<endl; 
        
    }
public:
    void ptr_give(){
        void (Cat::*p)();
        p=speak;
        cout<<*(int*)&p<<endl;
    }


};
void do_speak(Animal &animal){//传入类型为animal,animal的子类传入也不会报错
    animal.speak();//实现方法为子类的特殊实现
}
int main(){
    Cat c;
    Animal a;
    cout<<"add of a "<<&a<<endl;
    int addr=*(int *)*(int *)(&a);  //虚表地址
    int faddr = *(int *)*(int *)(&a);//a::speeak()地址
    int f1addr = *((int *)(*(int *)(&a))+ 2);
    int faddr_c = *(int *)*(int *)(&c);//c::speeak()地址
    int f1addr_c = *((int *)(*(int *)(&a))+ 2);//c::speak_1()
    cout<<"add of vtbl "<<addr<<endl;
    cout<<"add of speak "<<faddr<<endl;
    cout<<"add of speak__1 "<<f1addr<<endl;
    cout<<"add of speak from cat "<<faddr_c<<endl;
    cout<<"add of speak__1 from cat "<<f1addr_c<<endl;
    ((void(*)(void))f1addr)();
    system("pause");
    return 0;
}

 输出为:

add of a 0x61fdf8
add of vtbl 4206256
add of speak 4206256//父类
add of speak__1 4206320//父类的speak_1指针
add of speak from cat 4206192//重写了就不一样了  虚函数覆盖 
add of speak__1 from cat 4206320//cat子类没有重写父类的这个方法,所以他的虚函数表中的对应指针是与父类相同的
bulk!
//虚函数表中,按照声明顺序,父类的虚函数在前,子类在后,重写父类的直接替换原位置

猜测override关键字是编译器检查该虚函数的父类中有相同名字的,而纯虚函数则是检查

以上代码部分参考:C++知识积累:如何获取虚函数表以及虚函数地址_编译时虚函数地址-CSDN博客

虚函数详解-CSDN博客 

贴一个写的很好的虚函数解析

C++中有这样的约束:执行子类构造函数之前一定会执行父类的构造函数;同理,执行子类的析构函数后,一定会执行父类的析构函数,这也是为什么我们一直建议类的析构函数写成虚函数的原因。
  如果基类析构函数设置为虚函数,则父类指针实际指向的是子类的析构函数,执行子类的析构函数必然会执行父类析构函数,这样父类跟子类资源都得到释放。
————————————————
版权声明:本文为CSDN博主「qq_25427995」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_25427995/article/details/126690153 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值