C++的多态分为 : 静态多态与动态多态
静态多态是在编译器编译的时候就设置好了的,例如函数重载就是静态多态,编译器在编译期间根据函数的参数来选择适合的函数。
动态多态是在编译器运行的时候,在基类与子类有同名函数的情况下,根据接收的对象从而调用接收对象的同名函数。
另外要记得:根据赋值兼容性原则,子类的对象可以赋值给父类指针。
(我之前被多态搞晕就是因为忘记把这个原则带入进去,个人以为多态就是基于这个原则产生)
在一般情况下,如果父类的指针接收了子类的对象,如果调用同名函数,那么会调用父类指针的函数。
我们要做的是父类指针接收了子类的对象,如果调用同名函数,那么会调用子类对象的函数。
如下面的例子:(摘自 : https://www.cnblogs.com/cxq0017/p/6074247.html)
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
class Father
{
public:
void Face()
{
cout << "Father's face" << endl;
}
void Say()
{
cout << "Father say hello" << endl;
}
};
class Son:public Father
{
public:
void Say()
{
cout << "Son say hello" << endl;
}
};
void main()
{
Son son;
Father *pFather=&son; // 隐式类型转换
pFather->Say();
}
输出结果:
所以我们要使用virtual关键字修饰 父类的同名函数,被virtual修饰过的函数叫做虚函数,子类的同名函数不做修改。
如下面的例子:(摘自 : https://www.cnblogs.com/cxq0017/p/6074247.html)
#include "stdafx.h"
#include <iostream>
#include <stdlib.h>
using namespace std;
class Father
{
public:
void Face()
{
cout << "Father's face" << endl;
}
virtual void Say()
{
cout << "Father say hello" << endl;
}
};
class Son:public Father
{
public:
void Say()
{
cout << "Son say hello" << endl;
}
};
void main()
{
Son son;
Father *pFather=&son; // 隐式类型转换
pFather->Say();
}
输出结果:
多态成立的条件:
① 要有继承
② 要有函数重写(父类设置为虚函数),父类加了virtual叫多态,不加叫重定义。
③ 要有父类指针指向子类。
纯虚函数:
纯虚函数在需函数的前面加上 = 0 则代表纯虚函数,例如
class Father
{
public:
virtual void Say() = 0;
};
含有纯虚函数的类叫做抽象类,该类无法实例化,纯虚函数也不能有函数体。
纯虚函数我个人赶紧与虚函数没有任何关系,只是名字叫纯虚函数而已。
纯虚函数的作用: 主要是用于作为接口,定义这种纯虚函数主要是用于企业的接口规范。当子类继承它的时候,由子类完成纯虚函数的函数体。(你可以理解成纯虚函数就是头文件,继承纯虚函数的子类就是引用了头文件的cpp)。另外子类是可以继承多个接口的。
多态原理:
当类中声明虚函数的时候,编译器会在类中生出一个虚函数表。
虚函数表是存储成员函数指针的数据结构,由编译器自动生成与维护,添加了virtual关键字的成员函数的指针都会被存储到虚函数表,每个类都有一个虚函数表。
当初始化带有虚函数的类的对象的时候,每个对象都有一个指向虚函数表的指针 — vptr 指针
虚函数属于类,虚函数指针属于对象。
另外,虚函数表是可以继承的。
在子类没有重写与父类的同名函数的情况下,如果子类继承了基类的虚函数表,那么子类的虚函数表中就包含着基类的所有虚函数表的项。那么如果子类使用virtual修饰了同名函数,那么子类中的虚函数表对应的同名函数的地址就会改变,指向自己的同名函数。如果子类再添加了虚函数,那么子类的虚函数表就会在下面再添加一项。