多态
多态是面向对象的非常重要的一个特性
同一操作可以作用于不同对象,可以有不同的解释,产生不同的执行效果
在运行时,可以识别出真正的对象类型,调用对应子类中的函数
多态的要素:
- 子类重写父类的成员函数(override)
- 父类指针指向子类对象
- 利用父类指针调用重写的成员函数
背景
先看一个情况,当一个函数需要传不同的对象参数时,用重载函数写可扩展性差,太麻烦
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| struct Dog { void speak() { cout << "dog::speak" << endl; } void run() { cout << "dog::run" << endl; } }; struct Cat { void speak() { cout << "cat::speak" << endl; } void run() { cout << "cat::run" << endl; } }; struct Pig { void speak() { cout << "pig::speak" << endl; } void run() { cout << "pig::run" << endl; } };
void liu(Dog* p) { p->run(); p->speak(); } void liu(Cat* p) { p->run(); p->speak(); } void liu(Pig* p) { p->run(); p->speak(); }
int main() { liu(new Dog()); liu(new Cat()); liu(new Pig()); return 0; }
|
默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态
1 2 3 4 5 6 7
| Animal* p = new Dog(); p->speak(); p->run();
|
1 2 3 4 5 6 7
| Cat* p = (Cat*)new Dog(); p->speak(); p->run();
|
以上就很离谱,反汇编查看,直接调用cat::speak
虚函数
C++中多态通过虚函数实现
虚函数:被 virtual 修饰的成员函数
只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(子类中可以省略virtual关键字)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| struct Animal { virtual void speak() { cout << "animal::speak" << endl; } virtual void run() { cout << "animal::run" << endl; } };
struct Dog : Animal{ void speak() { cout << "dog::speak" << endl; } void run() { cout << "dog::run" << endl; } }; struct Cat : Animal{ void speak() { cout << "cat::speak" << endl; } void run() { cout << "cat::run" << endl; } }; struct Pig : Animal{ void speak() { cout << "pig::speak" << endl; } void run() { cout << "pig::run" << endl; } };
void liu(Animal* p) { p->run(); p->speak(); }
int main() { liu(new Dog()); liu(new Cat()); liu(new Pig());
return 0; }
|
虚表
虚函数的实现原理是虚表,这个虚表里面存储的最后需要调用的虚函数地址,这个虚表也叫虚函数表
汇编分析:
虚析构函数
如果有父类指针指向子类对象时,应该将析构函数声明为虚函数(虚析构函数)
delete父类指针时,才会调用子类,保证析构的完整性
纯虚函数 & 抽象类
没有函数体且初始化为0的虚函数,用来定义接口规范
1 2 3 4
| struct Animal { virtual void speak() = 0; virtual void run() = 0; };
|
含有纯虚函数的类,叫做抽象类
- 不可以实例化(不可以创建对象)
- 抽象类也可以包括非纯虚函数、成员变量
- 如果父类是抽象类,子类没有完全重写纯虚函数,那么子类依然是个抽象类