" 多态 " 实现需要满足以下三个条件 :
满足 ① 继承 , ② 虚函数重写 , ③ 父类指针/引用指向子类对象 三个条件 , 即可实现多态 ;
" 多态 " 的机制 , 由 " 虚函数表 " 实现 ;
" 虚函数表 " , 英文名称为 " Virtual Function Table " , 简称 Vtable ;
C++ 编译器 通过将 虚函数指针 放入 基类 的 虚函数表中 , 实现在 运行时 根据实际对象的类型 来调用对应的 virtual 虚函数 ;
虚函数表 是由 C++ 编译器 自动维护的 , 对 程序员 透明 ;
" 虚函数表 " 由 C++ 编译器 负责 创建 与 维护 , 被 virtual 关键字 修饰的 虚函数 , 会自动 被 C++ 编译器 存储到 " 虚函数表 " 中 ;
虚函数表 创建 : 在 类 中使用 virtual 关键字 声明 虚函数 时 , C++ 编译器 会自动为该类生成 " 虚函数表 " ;
虚函数表 存储 虚函数指针 : " 虚函数表 " 是 存储 " 类成员函数指针 " 的 数据结构 , 是一个 函数指针数组 , 数组中的元素都是函数指针 , 具体存储的都是 指向 类中的虚函数 的指针 ;
如果 C++ 类中存在 virtual 虚函数 , 在创建对象时 , 会生成 虚函数表 Virtual Function Table , 简称 vtable ;
C++ 编译器 编译 代码时 , 会自动为该类 添加 一个 vptr 指针 成员变量 , 该指针 会指向 虚函数表 ;
" 虚函数表 " 在 C++ 编译器 编译 时 生成 , 运行时 存储在可执行文件的内存中 ;
程序运行时 , 根据对象的类型信息 , 可以通过 虚函数表 来动态地调用对应的函数 ;
虚函数表 与 对象 是一一对应的 , 如果 父类指针 指向 的对象 , 调用 虚函数 , 则会去 对象对应的 虚函数表 中查找函数 , 找到的肯定是 对象的 虚函数 ;
虚函数表机制 可以避免在运行时进行类型判断 , 提高了程序的效率和可维护性 ;
C++ 编译器 确定 函数 是否为 virtual 虚函数 ;
在下面的代码中 , Parent 是父类 , 其中定义了 virtual 虚函数 , Child 子类中重写了该 虚函数 ;
使用 如下代码 , 创建 Child 子类对象时 , 发现有 virtual 虚函数 会创建 虚函数表 , 在对象中会自动添加 vptr 指针 成员变量 指向 虚函数表 首地址 ;
// 创建 Child 子类对象时
// 发现有 virtual 虚函数 会创建 虚函数表
// 在对象中使用 vptr 指针指向 虚函数表 首地址
Child c;
虚函数表 中 存储了 多个 虚函数 的 入口地址 ;
将 Child 对象的地址 赋值给 Parent * 指针时 , 通过 Parent* 指针调用 fun 虚函数 ,
C++ 编译器 根本不会去关心 当前调用的函数 是 Parent 父类的 还是 Child 子类的 ,
而是根据对象中的 vptr 指针 指向的 虚函数表 调用 对应的 虚函数 ;
父类对象 和 子类对象 中 都有一个 vptr 指针 成员变量 ,
当调用 虚函数 时 , 会根据对象中的 vptr 指针查找 虚函数表 中的 对应 虚函数 ;
代码示例 :
#include "iostream"
using namespace std;
// 父类
class Parent {
public:
virtual void fun(int a)
{
cout << "执行 父类 virtual void fun(int a) 函数" << endl;
}
};
// 子类
class Child : public Parent {
public:
virtual void fun(int a)
{
cout << "执行 子类 virtual void fun(int a) 函数" << endl;
}
};
int main() {
Parent* p;
// 创建 Child 子类对象时
// 发现有 virtual 虚函数 会创建 虚函数表
// 在对象中使用 vptr 指针指向 虚函数表 首地址
Child c;
// 将父类指针指向子类对象
p = &c;
// 通过父类指针调用子类对象的 fun 函数
p->fun(1);
// 控制台暂停 , 按任意键继续向后执行
system("pause");
return 0;
}
执行结果 :
执行 子类 virtual void fun(int a) 函数
Press any key to continue . . .