动态多态原理浅析(C++)

2022-10-27 14:05:19 浏览数 (2)

前言

多态主要分为两类:

  • 静态多态:地址早绑定,即编译阶段确定函数地址,例如函数重载运算符重载
  • 动态多态:地址晚绑定,即运行阶段确定函数地址

动态多态

使用条件

父类指针或引用指向子类对象

基础语法

引入一段代码示例:

代码语言:javascript复制
#include<iostream>
using namespace std;
class Animal
{
public:
    void speak()
    {
        cout << "动物在叫" << endl;
    }
};
class Cat :public Animal
{
public:
    void speak()
    {
        cout << "猫在叫" << endl;
    }
};
void doSpeak(Animal& animal)    //Animal& animal = cat
{
    animal.speak();     //地址早绑定
}
void test01()
{
    Cat cat;
    doSpeak(cat);   //父类引用/指针指向子类对象
    cout << "Animal类占用内存:" << sizeof(Animal) << endl;
}
int main()
{
    test01();
    system("pause");
}

运行结果为:

程序期望输出Cat类的speak方法,也即输出内容为猫在叫

可以看到实际输出与期望不符,原因是speak()在编译阶段已经确定了地址,无法通过父类指针指向子类对象,解决思路即是将早绑定改为晚绑定,让speak()在运行阶段正确指向子类对象,将Animal类的代码改成如下:

代码语言:javascript复制
class Animal
{
public:
    virtual void speak() //virtual修饰后变成虚函数
    {
        cout << "动物在叫" << endl;
    }
};

修改后的运行结果为:

加入virtual关键词后,speak()变为虚函数,子类中的speak()无需添加virtual关键字(但也为虚函数)

此时speak()正确指向Cat类,由此可以得出多态满足的条件:

  • 有继承关系
  • 子类重写父类中的虚函数

修改之前的Animal类占用内存为1字节,说明此时为空类(空类占用1字节),修改之后的占用变为4字节,可知加入virtual关键词后,类内部结构发生了变化,不难猜出此时类中存在一个指针

实现原理

为了直观看到Animal类内部结构,借助VS自带的命令提示工具,到源文件存放目录后运行cl /d1 reportSingleClassLayoutAnimal test.cpp,结果如下:

可以看到类内存在一个大小为4的vfptr,也即是virutal function pointer-虚函数(表)指针,该指针指向下方的vftable,也即是virtual function table-虚函数表,该表中存放的speak()实际地址是&Animal::speak

重复上述操作,继续查看Cat类内部结构:

可以看到重写后,Cat类中的虚函数表内实际存储的函数地址为&Cat::speak

图示

为方便理解,另附一张示意图

0 人点赞