写在开头
本篇文章适用于C++初学者,仅讨论一般条件下的多态实现原理,所有语言多态的原理基本上都是这个套路,抽象类的多态不在讨论范围之内,作者学习编程一周年第一次发文章,如有疏漏还请批评指正,另外求一个Unity大神教教我四元数的计算吧,真的给我难哭了😭😭😭。
多态的条件
首先多态是发生在一段公共继承关系中的,保护继承与私有继承无法实现多态,子类必须有和父类同名的函数,父类的同名函数必须使用virtual声明为虚函数,这就是多态的前提条件。
多态的作用
因为多态是发生在继承关系中的,那么父类的指针和引用就能够指向子类对象。
先看代码
class Base //父类
{
public:
void virtual Print() //父类同名函数声明为虚函数
{
cout << "Hello" << endl;
}
};
class Drive : public Base //子类继承父类
{
public:
void Print() //子类的同名函数,由于父类声明为虚函数,此处子类重写(覆盖)了父类的虚函数
{
cout << "World" << endl;
}
};
void Func(Base& base) //使用父类引用接收传入对象
{
base.Print(); //通过父类引用调用方法
}
int main()
{
Base b_1;
Drive d_1;
Func(b_1); //传入父类对象,输出Hello
Func(d_1); //传入子类对象,输出World
}
以上代码实现了一个最基本的多态,运行代码后可以看出,经由父类指针调用的Print方法输出了不同的结果,这就是多态的基本作用,它能够提高代码的可维护性与拓展性,使程序的维护变得更加灵活(更加的难理解,学习成本更高😅)。
多态的原理
首先要明白virtual关键字的作用,在以上代码中父类的Print函数被修饰为虚函数,代码在编译后虚函数就会变成虚函数指针(vfptr),指向虚函数表(vftbl)这个vftbl其实是一个指针数组,内部存放虚函数的地址,不管类中含有多少个虚函数,都只会生成1个虚函数指针,通过这个指针访问的虚函数表中存放了类中所有虚函数的地址。
子类继承父类时也会继承其vfptr,因为子类含有和父类同名的函数,且父类的函数是虚函数,那么子类的同名函数就会由原本的重定向(也称隐藏,发生在当父类同名函数不是虚函数时)变为重写(覆盖),每个含有虚函数的类都有一个自己的虚函数表。当一个类被继承时,如果子类没有重写父类的虚函数,那么子类的虚函数表中就会保存父类的虚函数地址,如果子类重写了父类的虚函数,那么子类的虚函数表中就会保存子类自己的虚函数地址。这样,当使用基类指针调用虚函数时就可以根据指针所指向的对象的类型来动态地确定调用哪个版本的虚函数,实现多态。
总结
父类面对一个对象时有自己的方法,子类继承后面对这个对象表现出了不同的方法,这就是多态,通过子类重写父类的虚函数实现。
多态与传入参数的类型无关,与调用的对象有关,多态的本质就是不管Base& base指向谁,直接去这个对象的虚函数表中调用对应的虚函数,而子类的虚函数在重写时就覆盖了其父类的虚函数,因此实现了多态
写在最后
好久没写C++了,这篇文章也相当于复习,凌晨5点睡不着闲着没事在手机上码了这么多字,用在线编译器跑了好多遍测试代码都没通过,10点起床用自己的vs跑了一遍就过了,发现是继承时没加public。