一.什么是多态
用父类的指针指向子类的实例,然后通过父类的指针调用子类的成员函数,一般有重写、重
载
重写是动态多态,重载是静态多态(编译器在编译期完成)
多态的使用有三个前提条件:
- 公有继承
- 函数覆盖
- 基类的引用/指针指向派生类对象
1.函数覆盖
函数覆盖、函数隐藏。这两个比较相似,但是函数隐藏不支持多态,而函数覆盖是多态的前提条件。函数覆盖比函数隐藏有一下几点区别:
- 函数隐藏是派生类中存在与基类中同名同参的函数,编译器会将基类的同名同参数的函数进行隐藏。
- 函数覆盖是基类中定义了一个虚函数,派生类编写一个同名同参数的函数将基类中的虚函数进行重写并覆盖。注意:覆盖的基类函数必须是虚函数。
2.虚函数
一个函数使用virtual关键字修饰,就是虚函数。虚函数是函数覆盖的前提。
虚函数的性质
虚函数具有传递性,基类中的覆盖的函数是虚函数,派生类中新覆盖的函数也是虚函数。
只有普通成员函数与析构函数可以声明为虚函数
可以在派生类的新覆盖的函数上使用override关键字验证覆盖是否成功
3.基类的指针/引用指向派生类对象
#include <iostream>
using namespace std;
class Animal
{
public:
// 虚函数
virtual void eat()
{
cout << "动物爱吃饭" << endl;
}
};
class Dog :public Animal
{
public:
void eat()override
{
cout << "狗爱吃骨头" << endl;
}
};
class Cat :public Animal
{
public:
void eat()override
{
cout << "猫爱吃鱼" << endl;
}
};
int main()
{
Animal *a1 = new Dog;
Animal *a2 = new Cat;
a1->eat(); // 狗爱吃骨头
a2->eat(); // 猫爱吃鱼
return 0;
}
4.虚析构函数
如果不适用虚析构函数,且基类指针或引用指向派生类对象,使用delete销毁对象时,只能触发基类的析构函数,如果在派生类中申请内存等资源,则会导致无法释放,出现内存泄漏的问题。
解决方案给基类的析构函数使用virtual修饰为虚析构函数,通过传递性可以把各个派生类的析构函数都变为虚析构函数,因此建议给一个可能为基类类中的析构函数设置成虚析构函数。
#include <iostream>
using namespace std;
class Animal
{
public:
// 虚析构函数
virtual ~Animal()
{
cout << "析构函数Animal" << endl;
}
};
class Dog :public Animal
{
public:
~Dog()
{
cout << "析构函数Dog" << endl;
}
};
int main()
{
Animal *a1 = new Dog;
delete a1;
return 0;
}