- 一.多态的原理
- 1.什么是多态
- 1.1多态的概念
- 2.1动态多态和静态多态
- 2.动态多态实现的具体过程
- 2.1多态的条件
- 2.2重写
- 2.3虚函数
- 2.4动态多态实现过程
- 二.仿写多态
- 1.类成员函数指针
- 1.1函数调用的两种方式
- 1.2函数指针的好处
- 2.代码实现及思路
- 2.1父类指针指向子类对象
- 2.2子类对象函数地址覆盖父类对象函数地址
- 2.3成功仿写动态多态的实现
在仿写多态之前我们必须先简单了解一下多态是什么?
多态就是相同的行为方式导致了不同的行为结果,即产生了多种形态,即多态
多态分为静态多态和动态多态
静态多态是编译期的概念,是通过函数重载,模板方法来实现的
动态多态是运行期的概念,是通过继承和虚函数实现的
2.动态多态实现的具体过程 2.1多态的条件1.必须存在继承关系
2.父类指针指向子类对象,且用这个父类指针调用了虚函数
3.父类中存在虚函数,且继承该父类的子类重写了父类的虚函数
2.2重写在继承的条件下,子类定义了和父类中某个虚函数一模一样的虚函数(函数名,函数参数列表(参数个数,参数类型),返回值)我们就称之为重写
2.3虚函数虚函数是实现多态必不可少的条件之一
当一个类中存在虚函数时,定义类的对象的内存空间首地址会多出一块内存(4个字节)在存放虚函数指针(vfptr),这个虚函数指针是一个二级指针(void),这个虚函数指针指向了一个数组,这个数组我们称之为虚函数列表(vftable),虚函数指针是属于对象的,只有在定义对象时才会存在,虚函数指针是属于类的,编译期就存在了,但是必须通过对象才能调
2.4动态多态实现过程动态多态的具体实现过程
1)父类要有虚函数,父类就有一个虚函数表
2)子类继承父类,子类就有一个虚函数表,
子类重写虚函数,就会覆盖虚函数表里面虚函数地址
3)定义子类对象,子类对象的前4个字节就会有一个虚指针,虚指针指向虚函数表,父类指针指向子类对象
4)父类指针调用虚函数,就会根据子类对象的前四个字节,得到虚指针,进而得到虚函数表的入口地址,通过遍历虚函数表,
找到对应的虚函数并调用,从而完成多态.
调用函数的两种方式:通过函数名直接调用、通过函数指针间接调用
1.2函数指针的好处通过函数指针调用的好处:函数指针可以将实现同一功能的多个模块统一起来标识,使系统结构更加清晰,后期更容易维护,提高代码的复用性,降低耦合度
2.代码实现及思路最终目的:父类指针指向子类对象,子类的函数地址覆盖父类函数地址,当调用父类函数时,实现的是子类的功能
2.1父类指针指向子类对象父类指针指向子类对象调用的是父类的功能
#includeusing namespace std; class CFather { public: void show() { cout << "父类的show方法" << endl; } }; class CSon :public CFather { public: void show() { cout << "子类的show方法" << endl; } }; int main() { CFather* pFa = new CSon;//父类指针指向子类对象 pFa->show(); return 0; }
运行结果(请忽略自带的水印):
上面说过调用函数有两种方式,分别是通过函数名直接调用、通过函数指针间接调用
这里我们选择第二种,函数指针调用的方式
void (CFather:: * p_fun)() = (void (CFather::*)()) & CSon::show;//子类函数地址覆盖了父类函数的地址2.3成功仿写动态多态的实现
#includeusing namespace std; class CFather { public: void show() { cout << "父类的show方法" << endl; } }; class CSon :public CFather { public: void show() { cout << "子类的show方法" << endl; } }; int main() { CFather* pFa = new CSon;//父类指针指向子类对象 //pFa->show(); void (CFather:: * p_fun)() = (void (CFather::*)()) & CSon::show;//子类函数地址覆盖了父类函数的地址 (pFa->*p_fun)();//通过函数指针的方式调用show函数, //但是父类的show函数地址已经被子类的show函数地址覆盖,从而调用了子类的show函数,从而实现了多态 return 0; }
运行结果(请忽略自带的水印):



