一文带你了解多态的本质

首先由一个简单的例子引出多态(为了演示方便使用了struct关键字来定义类)

#include <iostream>
 using namespace std;
 ​
 struct Cat
 {
     void speak() {
         cout << "Cat::speak()" << endl;
     }
     void run() {
         cout << "Cat::run()" << endl;
     }
 };
 ​
 struct Dog
 {
     void speak() {
         cout << "Dog::speak()" << endl;
     }
     void run() {
         cout << "Dog::run()" << endl;
     }
 };
 ​
 struct Pig
 {
     void speak() {
         cout << "Pig::speak()" << endl;
     }
     void run() {
         cout << "Pig::run()" << endl;
     }
 };
 ​
 void walk(Dog* dog) {
     dog->speak();
     dog->run();
 }
 ​
 void walk(Cat* cat) {
     cat->speak();
     cat->run();
 }
 ​
 void walk(Pig* pig) {
     pig->speak();
     pig->run();
 }
 ​
 int main(){
     walk(new Dog());
     walk(new Cat());
     walk(new Pig());
     return 0;
 }

假如新增一个动物类,又得重载一个walk函数,导致代码越写越多,有什么好的方法能解决这个问题吗?

我们一开始想到的是把共同的函数都放到一个父类中,再由子类继承该父类然后在其类中重写该函数,最后借助父类指针可以指向子类对象的性质,将walk函数中的参数改为父类指针

于是我们很高兴地写出了方案二的代码

#include <iostream>
 using namespace std;
 ​
 struct Animal
 {
     void speak() {
         cout << "Animal::speak()" << endl;
     }
     void run() {
         cout << "Animal::run()" << endl;
     }
 };
 ​
 struct Cat : Animal
 {
     void speak() {
         cout << "Cat::speak()" << endl;
     }
     void run() {
         cout << "Cat::run()" << endl;
     }
 };
 ​
 struct Dog : Animal
 {
     void speak() {
         cout << "Dog::speak()" << endl;
     }
     void run() {
         cout << "Dog::run()" << endl;
     }
 };
 ​
 struct Pig : Animal
 {
     void speak() {
         cout << "Pig::speak()" << endl;
     }
     void run() {
         cout << "Pig::run()" << endl;
     }
 };
 ​
 void walk(Animal* animal) {
     animal->run();
 }
 ​
 int main(){
     walk(new Dog());
     walk(new Cat());
     walk(new Pig());
     return 0;
 }

当我们以为我们写的代码؏؏☝ᖗ乛◡乛ᖘ☝؏؏的时候,现实却给了我们一记重拳

什么?竟然不行,是哪里出了什么问题吗?为什么打印出来的结果都是父类中的结果呢?

我们在walk函数处下断点,然后按F5快捷键进入调试模式,在调试模式下按下alt+g快捷键进入反汇编模式,可以看到以下汇编代码

从图中我们可以看出,代码是直接写死的,直接就是call Animal::speak和call Animal::run,所以调用的当然是父类中的speak和run函数啦

我们需要的效果是同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,此时多态出场了

即在父类函数中使用virtual关键字加在共有的函数前

此时我们就写出了第三版代码

#include <iostream>
 using namespace std;
 ​
 struct Animal
 {
     virtual void speak() {
         cout << "Animal::speak()" << endl;
     }
     virtual void run() {
         cout << "Animal::run()" << endl;
     }
 };
 ​
 struct Cat : Animal
 {
     void speak() {
         cout << "Cat::speak()" << endl;
     }
     void run() {
         cout << "Cat::run()" << endl;
     }
 };
 ​
 struct Dog : Animal
 {
     void speak() {
         cout << "Dog::speak()" << endl;
     }
     void run() {
         cout << "Dog::run()" << endl;
     }
 };
 ​
 struct Pig : Animal
 {
     void speak() {
         cout << "Pig::speak()" << endl;
     }
     void run() {
         cout << "Pig::run()" << endl;
     }
 };
 ​
 void walk(Animal* animal) {
     animal->run();
 }
 ​
 int main(){
     walk(new Dog());
     walk(new Cat());
     walk(new Pig());
     return 0;
 }

此时调试模式下的汇编代码如下图所示(注意黄色框里的内容,call eax)

多态三要素

  • 子类override父类虚成员函数
  • 父类指针指向子类对象
  • 利用父类指针调用override的虚成员函数

重头戏来了,多态其底层是怎么实现的?虚指针+虚表

虚表里面存储着最终需要调用虚函数地址,这个虚表也叫虚函数表

下面将配合汇编代码来让大家快速的理解其本质

测试时用的代码如下(注意:以下代码是在x86环境下进行的测试,但原理都一样)

#include <iostream>
 using namespace std;
 ​
 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 = 23;
     cat->speak();
     cat->run();
     return 0;
 }

F5进入调试模式

调试时的内存布局如下

cat对象地址0x00F019D0

虚表地址0x00B3BD9C

虚表内容

0x00b219f1

0x00b219c4

按下alt+g进入反汇编模式

一直按F11,执行到下图红色方框处再按一次F11

就可以看到下图了,注意红色方框内的内容,再对比下上面写的虚表内容

然后再按一次,即可看到跳入到Cat类中执行speak函数

汇编代码解析

好了,到此就结束啦~相信大家对于多态又有了新的认识了吧 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值