如何通过虚函数实现多态?
多态分为静态多态和动态多态。C++ 中多态的实现使用的是动态绑定的技术,该技术的核心是虚函数表。首先在基类中用 virtual 关键字声明虚函数,编译器会为该类及子类生成专属的 “虚函数表(vtable)”,且每个实例化对象会隐藏一个 “虚表指针(vptr)”,指向所属类的虚函数表;接着让子类重写基类虚函数,并通过基类指针或引用指向子类对象;当调用虚函数时,程序不会在编译期绑定函数,而是在运行期通过对象的 vptr 找到对应类的 vtable,取出重写后的虚函数地址执行。最终实现 “同一接口,不同实现” 的多态效果。
- 虚函数:用 virtual 关键字声明的函数,称为 “虚函数”。当一个类(B)继承另一个类(A)时,类 B 会继承类 A 的函数的调用权,所以如果一个父类包含了虚函数,那么其子类也可调用这些虚函数。
- 虚函数表:每个包含虚函数的类都会有一个虚函数表。虚函数表是一个存放指针的数组,其内的每个元素都会对应一个虚函数的函数指针。虚函数表在编译阶段由编译器为每个包含虚函数的类生成,并且全局唯一(即同一个类的所有对象共享该类的虚函数表)。但子类的虚函数表是在父类虚函数表的基础上构建的:
- 若子类未重写父类的虚函数,子类的虚函数表中对应位置会直接复用父类虚函数的地址。
- 若子类重写了父类的虚函数,子类的虚函数表中会用自己的虚函数地址覆盖父类对应位置的地址。
- 若子类新增了自己的虚函数,这些虚函数地址会被追加到子类虚函数表的末尾(父类虚函数表不包含这些新增函数)。
- 虚表指针:为了指定每个对象所使用的虚函数表,每个对象内部都会包含一个虚表指针,来指向自己所使用的虚函数表。为了让每个包含虚函数表的类的对象都拥有一个虚表指针,编译器在类中添加了一个指针 vptr,用来指向虚函数表。这样,当类的对象在创建时便拥有了这个指针,且这个指针的值会自动被设置为指向该类的虚函数表(在构造函数中被赋值)。