目录
概述
- 多态在具体应用场景下可以理解为在同一个软件下,不同账号去登录,从而可以实现不同的功能。
- 多态的主要形式有两种,编译时的多态,即静态多态,另一种则是运行时的多态,即动态多态。
静态多态
函数重载实现,通过指定在同一个作用域中,定义多个函数名相同但是参数不同的函数
运算符重载,允许用户定义或者改变C++内置运算符的行为,从而实现多态行为
动态多态
继承与虚函数:继承允许派生类继承基类的属性和方法;虚函数则使得基类中函数可以被派生类重写。从而借助继承与虚函数配合,实现多态。
定义与实现
构成条件
静态多态构成条件总结
- 函数重载:
- 函数名相同:重载的函数必须具有相同的函数名
- 参数列表不同:重载函数的参数类型或者参数数量必须不同
- 返回类型无关:函数重载仅仅依赖于参数列表,与返回类型无关
- 运算符重载
- 内置运算符重定义:可以为类类型定义或重定义内置运算符行为
- 至少一个操作数是用户定义的类型
动态多态构成条件
- 继承关系:必须存在基类与派生类,拥有继承关系
- 虚函数:基类中至少包含一个虚函数,使用virtual关键字声明
- 函数重写:派生类必须重写基类中的虚函数
- 基类指针或引用:通过基类指针或引用调用虚函数
虚函数
虚函数定义:通过virtual 关键字声明虚函数目,派生类可以重写该函数,来实现特定功能
重写虚函数:派生类中通过重新定义虚函数,来提供其具体实现,派生类中这个函数仍然是虚函数
借助基类指针或者引用调用虚函数,实际调用的是派生类的重写版本
纯虚函数与抽象类:因为纯虚函数没有具体实现的虚函数,所以通常使用=0表示。包含纯虚函数的类为抽象类,不能够实例化对象。
虚函数表:每个包含虚函数的类,都有一张虚函数表,其中存储了这个类中所有虚函数的指针。在通过基类指针或者引用调用该函数的时候,程序会在运行时根据虚函数表找到实际调用的函数
作用分析:
- 动态绑定:运行的时候,根据对象的实际类型,调用正确的函数
- 多态性:允许基类指针或者引用调用派生类的实现
虚函数的特殊情况
协变返回类型:基类与派生类的返回类型不同,通常使用返回指针或者返回类型
(Derived :: clone 重写了Base:: clone 并返回一个Derived*类型的指针)
析构函数重写:确保在删除派类对象的时候能够正确调用派生类的析构函数,目的是保证资源正确释放避免内存泄漏
override final
override:检查派生类虚函数是否发生重写基类的某个虚函数,如果没有发生重写则报错。目的是帮助编译器检查函数是否与基类中匹配。
final:指示类或虚函数不能被进一步继承或者重写,从而防止派生类进一步改变函数行为,确保其特定的实现不被修改
重载、覆盖、隐藏
重载
- 同一作用域内,多个函数具有相同的名称,但是参数列表不同。编译器通过参数列表来区分这些函数。重载通常用于提高函数的灵活性和可读性。
- 特点
- 函数名相同
- 参数列表不同
- 与返回值无关
重写
- 派生类中重新定义基类的虚函数,函数名、参数列表、返回类型必须与基类中的虚函数完全相同,重写则是用于多态
- 特点
- 函数名、参数、返回类型相同
- 基类函数必须是虚函数
- 派生类函数必须使用相同函数名、参数与返回类型
重定义(隐藏):派生类中定义了与基类同名但是参数、返回类型不同的函数,基类的同名函数在派生类中被隐藏。非虚函数的重定义不会导致运行时的多态,而是会隐藏基类的同名函数。
- 特点
- 函数名相同
- 参数列表或返回类型不同
- 基类的同名函数被隐藏
抽象类
概念
抽象类是一种不能够被实例化的类,一般应用于定义接口,抽象类中至少有一个纯虚函数,该纯虚函数在基类中没有实现,必须在派生类中进行实现。
定义抽象类:虚函数声明后加上=0
接口继承与实现继承
接口继承:派生类继承基类的函数声明,并在派生类中实现这些接口,抽象类通常是这些接口继承的基础。
实现继承:派生类继承基类的具体实现。派生类可以直接使用基类的实现,同时也可以重写基类的虚函数提供新的功能
抽象类、接口继承、实现继承
- 抽象类
- 不能够实例化
- 至少存在一个纯虚函数
- 用于定义接口
- 接口继承
- 派生类继承基类的函数声明
- 派生类必须实现基类的纯虚函数
- 实现继承
- 派生类继承基类的具体实现
- 派生类可以直接使用基类的实现,也可以重写虚函数提供新的实现
多态原理
派生类继承基类的虚函数表(数组指针,存储着函数地址),若派生类重写了基类函数,则更新虚函数表中的指针指向。
虚函数表
虚函数表是一个数组指针,其中每一个指针都指向类的一个虚函数。每个包含虚函数的类又都有一个虚函数表,该表包含这该类虚函数的指针。对象通过虚函数指针访问虚函数表。
虚函数表的实现机制
- 类的虚函数表
- 每个包含虚函数的类,在编译的时候都会生成一个虚函数表,虚函数表中包含着指向该类所有虚函数的指针
- 对象的虚函数指针
- 每个对象在内存中有一个隐藏的指针,指向所属类的虚函数表。当通过该对象调用虚函数的时候,程序会自动通过虚函数指针找到虚函数表,从而调用适当的函数
派生类虚表的生成
- 派生类现将基类的虚表内容拷贝一份,放到派生类自己的虚表中
- 如果派生类重写了基类中的某个虚函数,用派生类自己的虚函数地址覆盖基类的虚函数地址
- 派生类自己新增加的虚函数,按照其在派生类中声明次序增加到派生类虚表的最后
- 存储:虚表中存储的是虚函数指针,虚函数存放在内存的代码区中
原理分析
派生类中存在一个指向基类的指针,该指针指向其虚函数表,虚函数表中存储这函数地址,其中如果派生类发生了重写,则派生类中继承虚函数地址会相应发生改变,最后根据对象类型通过虚函数表完成动态绑定。
原理总结
- 继承:派生类需要继承基类的接口和实现
- 虚函数:基类中声明虚函数,派生类从而重写这些虚函数
- 虚函数表:每个包含虚函数的类都有一个虚函数表,其中存储了该类所有的虚函数指针
- 虚函数指针:每个对象包含一个指向虚函数表的指针,当通过该对象调用虚函数的时候,程序会通过虚函数指针找到虚函数表,从而调用对应的函数
- 动态绑定:运行的时候,根据对象的实际类型,通过虚函数表实现动态绑定
虚函数表地址验证
- 创建对象地址与虚函数表地址验证
- 虚函数表指针在创建的对象内存中
- 虚函数表内容
- 通过验证虚表中的地址验证虚函数表中存储的是虚函数指针
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() const {
cout << "Base class show function" << endl;
}
virtual void display() const {
cout << "Base class display function" << endl;
}
};
class Derived : public Base {
public:
void show() const override {
cout << "Derived class show function" << endl;
}
void display() const override {
cout << "Derived class display function" << endl;
}
};
void printVTable(Base* obj) {
// 假设虚函数指针位于对象的起始位置
auto vptr = *(reinterpret_cast<void***>(obj));
cout << "V-Table address: " << vptr << endl;
// 假设有两个虚函数,打印虚函数表中的函数地址
for (int i = 0; i < 2; ++i) {
cout << "Function [" << i << "] address: " << vptr[i] << endl;
}
}
int main() {
Derived d;
cout << "Derived object address: " << &d << endl;
printVTable(&d);
return 0;
}
虚基表验证(继承)
- 八字节:多态偏移量和虚继承的基类成员在子类当中的偏移量
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() const {
cout << "Base class show function" << endl;
}
};
class Derived1 : virtual public Base {
public:
void show() const override {
cout << "Derived1 class show function" << endl;
}
};
class Derived2 : virtual public Base {
public:
void show() const override {
cout << "Derived2 class show function" << endl;
}
};
class MostDerived : public Derived1, public Derived2 {
public:
void show() const override {
cout << "MostDerived class show function" << endl;
}
};
void printVTable(Base* obj) {
auto vptr = *(reinterpret_cast<void***>(obj));
cout << "V-Table address: " << vptr << endl;
// 假设有多个虚函数,打印虚函数表中的函数地址
for (int i = 0; i < 3; ++i) {
cout << "Function [" << i << "] address: " << vptr[i] << endl;
}
}
void printVBaseTable(void* obj) {
// 虚基表指针可能存储在对象的某个位置,具体取决于编译器实现
auto vbptr = *(reinterpret_cast<void***>((char*)obj + sizeof(void*)));
cout << "V-Base Table address: " << vbptr << endl;
// 打印虚基表中的内容
for (int i = 0; i < 2; ++i) {
cout << "V-Base [" << i << "] address: " << vbptr[i] << endl;
}
}
int main() {
MostDerived md;
cout << "MostDerived object address: " << &md << endl;
// 打印Derived1部分的虚表
Derived1* d1 = &md;
cout << "Derived1 V-Table:" << endl;
printVTable(d1);
// 打印Derived2部分的虚表
Derived2* d2 = &md;
cout << "Derived2 V-Table:" << endl;
printVTable(d2);
// 打印虚基表
cout << "V-Base Table:" << endl;
printVBaseTable(d1);
return 0;
}
单继承与多继承的虚函数
单继承的虚函数
单继承则是一个类继承自另外一个类,每个包含虚函数的类都有一个虚函数表,派生类的虚函数表指向自己的虚函数实现
实验说明:Derived继承自Base,重写了show函数,所以Derived这个类覆盖了Base::show的地址
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() const {
cout << "Base 类的 show 函数" << endl;
}
};
class Derived : public Base {
public:
void show() const override {
cout << "Derived 类的 show 函数" << endl;
}
};
void printVTable(void* obj) {
auto vptr = *(reinterpret_cast<void***>(obj));
cout << "虚函数表地址: " << vptr << endl;
for (int i = 0; vptr[i] != nullptr; ++i) {
cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
}
}
int main() {
Base b;
Derived d;
cout << "Base 对象地址: " << &b << endl;
printVTable(&b);
cout << endl;
cout << "Derived 对象地址: " << &d << endl;
printVTable(&d);
return 0;
}
多继承的虚函数
一个类继承自多个基类,每个基类都会有自己的虚函数表,派生类包含多个虚函数表指针,分别指向各个基类的虚函数表
实验解释:Derived类中有两个虚函数表指针,其分别是指向Base1 和 Base2的虚函数表。又因为在多继承中,Derive类重写了两个基类的函数,所以这两个虚表中包含的是Derived::show 和 Derived::display的地址
#include <iostream>
using namespace std;
class Base1 {
public:
virtual void show() const {
cout << "Base1 类的 show 函数" << endl;
}
};
class Base2 {
public:
virtual void display() const {
cout << "Base2 类的 display 函数" << endl;
}
};
class Derived : public Base1, public Base2 {
public:
void show() const override {
cout << "Derived 类的 show 函数" << endl;
}
void display() const override {
cout << "Derived 类的 display 函数" << endl;
}
};
void printVTable(void* obj) {
auto vptr = *(reinterpret_cast<void***>(obj));
cout << "虚函数表地址: " << vptr << endl;
for (int i = 0; vptr[i] != nullptr; ++i) {
cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
}
}
int main() {
Base1 b1;
Base2 b2;
Derived d;
cout << "Base1 对象地址: " << &b1 << endl;
printVTable(&b1);
cout << endl;
cout << "Base2 对象地址: " << &b2 << endl;
printVTable(&b2);
cout << endl;
cout << "Derived 对象地址: " << &d << endl;
cout << "Derived 继承自 Base1 的虚函数表:" << endl;
Base1* pb1 = &d;
printVTable(pb1);
cout << endl;
cout << "Derived 继承自 Base2 的虚函数表:" << endl;
Base2* pb2 = &d;
printVTable(pb2);
return 0;
}
菱形继承的虚函数
#include <iostream>
using namespace std;
class Base {
public:
virtual void show() const {
cout << "Base 类的 show 函数" << endl;
}
};
class Derived1 : virtual public Base {
public:
void show() const override {
cout << "Derived1 类的 show 函数" << endl;
}
};
class Derived2 : virtual public Base {
public:
void show() const override {
cout << "Derived2 类的 show 函数" << endl;
}
};
class MostDerived : public Derived1, public Derived2 {
public:
void show() const override {
cout << "MostDerived 类的 show 函数" << endl;
}
};
void printVTable(void* obj) {
auto vptr = *(reinterpret_cast<void***>(obj));
cout << "虚函数表地址: " << vptr << endl;
for (int i = 0; vptr[i] != nullptr; ++i) {
cout << "函数 [" << i << "] 地址: " << vptr[i] << endl;
}
}
void printVBaseTable(void* obj) {
auto vbptr = *(reinterpret_cast<void***>((char*)obj + sizeof(void*)));
cout << "虚基表地址: " << vbptr << endl;
for (int i = 0; vbptr[i] != nullptr; ++i) {
cout << "虚基 [" << i << "] 地址: " << vbptr[i] << endl;
}
}
int main() {
Base b;
Derived1 d1;
Derived2 d2;
MostDerived md;
cout << "Base 对象地址: " << &b << endl;
printVTable(&b);
cout << endl;
cout << "Derived1 对象地址: " << &d1 << endl;
printVTable(&d1);
cout << endl;
cout << "Derived2 对象地址: " << &d2 << endl;
printVTable(&d2);
cout << endl;
cout << "MostDerived 对象地址: " << &md << endl;
cout << "MostDerived 继承自 Derived1 的虚函数表:" << endl;
Derived1* pd1 = &md;
printVTable(pd1);
cout << endl;
cout << "MostDerived 继承自 Derived2 的虚函数表:" << endl;
Derived2* pd2 = &md;
printVTable(pd2);
cout << endl;
cout << "虚基表:" << endl;
printVBaseTable(pd1);
return 0;
}