多态的定义
虚函数的重写
- 派生类中有一个跟基类完全相同的虚函数,其返回值类型、函数名、参数完全相同
- 协变:重写的虚函数返回值不同,但必须分别是基类指针和派生类指针或基类引用和派生类引用,协变我们很少用
- 派生类中重写虚函数可以不加virtual关键字,但在派生类中依旧保持虚函数属性,这种写法是不规范的
- 如果基类的析构函数时虚函数,那么派生类析构函数重写了基类的析构函数,编译器为析构函数的名称做了特殊处理,编译后函数名统一为destructor,所以基类的析构函数最好写成虚函数
重载、重写、重定义
抽象类
override/final
虚函数表
多态是通过调用对象自己的虚函数表中的虚函数指针找到对应的虚函数,这样不同对象完成同一行为,展现不同状态
#include <iostream>
using namespace std;
class Base1{
public:
virtual void Func1(){
cout << "Base1::Func1()" << endl;
}
virtual void Func2(){
cout << "Base1::Func2()" << endl;
}
void Func3(){
cout << "Base1::Func3()" << endl;
}
private:
int _base1 = 1;
};
class Base2{
public:
virtual void Func1(){
cout << "Base2::Func1()" << endl;
}
virtual void Func2(){
cout << "Base2::Func2()" << endl;
}
private:
int _base2 = 2;
};
//单继承
class Derive1 : public Base1{
public:
virtual void Func1(){
cout << "Derive1::Func1()" << endl;
}
virtual void Func4(){
cout << "Derive1::Func4()" << endl;
}
private:
int _derive2 = 4;
};
//多继承
class Derive2 : public Base1, public Base2{
public:
virtual void Func1(){
cout << "Derive2::Func1()" << endl;
}
virtual void Func4(){
cout << "Derive2::Func4()" << endl;
}
private:
int _derive2 = 4;
};
//遍历虚函数表中的虚函数指针
typedef void(*VFPTR)();
void PrintVfptr(VFPTR vfptr[]){
cout << "虚表地址" << vfptr << endl;
for (int i = 0; vfptr[i] != nullptr; ++i){
printf("第%d个函数地址:0X%x,->", i, vfptr[i]);
VFPTR f = vfptr[i];
f();
}
cout << endl;
}
int main(){
Base1 b1;
Derive1 d1;
Derive2 d2;
cout << "基类虚函数表:" << endl;
VFPTR* vfptr1 = (VFPTR*)(*(int*)&b1);
PrintVfptr(vfptr1);
cout << "单继承继承基类虚函数表:" << endl;
VFPTR* vfptr2 = (VFPTR*)(*(int*)&d1);
PrintVfptr(vfptr2);
cout << "多继承第一继承基类虚函数表:" << endl;
VFPTR* vfptr3 = (VFPTR*)(*(int*)&d2);
PrintVfptr(vfptr3);
cout << "多继承第二继承基类虚函数表:" << endl;
VFPTR* vfptr4 = (VFPTR*)(*(int*)((char*)&d2 + sizeof(Base1)));
PrintVfptr(vfptr4);
return 0;
}
- 存在虚函数的类中除成员外,还多了一个_vfptr虚函数表指针在前面(也可在后面,跟平台有关)
- 对象存放虚表指针,虚表指针指向虚函数表也称虚表,虚函数表存放虚函数指针
- 虚函数表本质是一个存虚函数指针的指针数组,这个数组最后面访了一个nullptr
- 虚函数表在VS下存在数据段中
- 派生类对象由基类继承下来的成员包括虚表指针和自己的成员两部分组成
- Func1完成了重写,Func2继承下来是虚函数放进虚表,Func3也继承下来,但是不是虚函数,所以不会放进虚表,编译器窗口隐藏了未重写的虚函数Func4
多继承派生类的未重写虚函数放在第一个继承基类部分的虚函数表中