4.7 多态
多态的基本概念
静态多态:函数重载和运算符重载。 动态多态:派生类和虚函数实现运行时多态。 区别: 静态多态函数地址绑定,编译阶段确定函数地址。 动态多态的函数地址晚绑定,运行阶段确定函数地址。
class Animal
{
public:
void speak(){
cout<<"动物在说话"<<endl;
}
};
class Cat: public Animal{
public:
void speak(){
cout<<"小猫在说话"<<endl;
}
};
class Dog: public Animal{
public:
void speak(){
cout<<"小狗在说话"<<endl;
}
};
//早绑定,绑定到Animal的speak方法。
//传入猫时,也是指向Animal的speak,输出动物在说话。
void doSpeak(Animal &animal){
animal.speak();
}
void test(){
Cat cat;
doSpeak(cat);
Dog dot;
doSpeak(dog);
}
而我们想要的效果是传入猫时,输出猫在说话。
只要在Animal的方法void speak()
前加上virtual关键字,变为virtual void speak()
,就改写为了虚函数。
虚函数就会在运行时确定函数地址。
总结: 动态多态满足条件:
- 子类继承父类
- 子类重写父类的虚函数 (注意,重写不是重载,重写的返回值、函数名、参数列表都相同)
动态多态的使用 父类的指针(或引用)指向子类的对象。
多态的原理
有虚函数的类会有一个vfptr (虚函数表指针),指向虚函数表。
虚函数表记录的是函数的入口地址。
子类继承父类,重写父类的虚函数后,改写了自己的虚函数表。
4.7.2 多态案例1——计算器
class AbstractCalculator
{
public :
virtual int getResult(){
return 0;
}
int num1;
int num2;
};
class AddCalculator: public AbstractCalculator
{
public :
int getResult(){
return num1 + num2;
}
};
class SubCalculator: public AbstractCalculator
{
public :
int getResult(){
return num1 - num2;
}
};
void test(){
AbstarctCalculator * abc = new AddCalculator;
abc->num1 = 10;
abc->num2 = 10;
cout<<abc.getResult()<<endl;
abc = new SubCalculator;
abc->num1 = 10;
abc->num2 = 10;
cout<<abc.getResult()<<endl;
}
好处:开闭原则 开发扩展,关闭修改。 在开发新功能时,不需要修改原来的代码。
4.7.3 纯虚函数和抽象类
有些函数永远不会被真正的执行,不需要函数体。我们可以写成纯虚函数。
纯虚函数
语法:在虚函数后面加=0;
virtual 返回值类型 函数名(参数列表) = 0;
有纯虚函数的类是抽象类。
抽象类无法实例化对象。
抽象类子类必须重写抽象类的纯虚函数,否则也是抽象类。
4.7.4 多态案例2-制作饮料
制作饮料的大致流程是:煮水-冲泡-倒入杯中-加入辅料
利用多态实现这个抽象过程,然后实现子类分别制作咖啡和茶叶。
class AbstractDrinking{
public:
virtual void Boil() = 0;
virtual void Brew() = 0;
virtual void PourInCup() = 0;
virtual void PutSomething() = 0;
void makeDrink(){
Boil();
Brew();
PourInCup();
PutSomething();
}
};
class Coffee: AbstractDrinking{
void Boil(){
cout<<"煮水"<<endl;
}
void Brew(){
cout<<"冲泡咖啡"<<endl;
}
void PourInCup(){
cout<<"倒入杯中"<<endl;
}
void PutSomething(){
cout<<"加糖"<<endl;
}
};
class Tea: AbstractDrinking{
void Boil(){
cout<<"煮开水"<<endl;
}
void Brew(){
cout<<"冲泡茶叶"<<endl;
}
void PourInCup(){
cout<<"倒入杯中"<<endl;
}
void PutSomething(){
cout<<"加枸杞"<<endl;
}
};
void doWork(AbstractDrinking* abs){
abs->makeDrink();
delete abs;
}
void test01(){
doWork(new Coffee);
cout<<"-----------"<<endl;
doWork(new Tea);
}
4.7.5 虚析构和纯虚析构
多态使用时,子类属性开辟到堆区时,父类指针在释放时(delete)不调用子类的析构函数。
解决方法:父类中使用虚析构或纯虚析构。
virtual ~Animal(){}
或virtual ~Animal() = 0;
注意这个纯虚函数也是要有实现的(回收父类中创建的内存空间)
Animal::~Animal() {
...
}
4.7.6 多态案例3-电脑组装
抽象实现电脑的3个主要组件(CPU、显卡、内存条),然后实现具体的生产厂家。 再实现一个电脑类,包含3个组件。
#include<iostream>
#include<string>
using namespace std;
class CPU
{
public:
virtual void calculate() = 0;
};
class VideoCard
{
public:
virtual void display() = 0;
};
class Memory
{
public:
virtual void store() = 0;
};
class Computer
{
public:
Computer(CPU * t_cpu, VideoCard * t_vc, Memory * t_mem ) {
cpu = t_cpu;
vc = t_vc;
memory = t_mem;
}
~Computer() {
if (cpu != NULL) {
delete cpu;
cpu = NULL;
}
if (vc != NULL) {
delete vc;
vc = NULL;
}
if (memory != NULL) {
delete memory;
memory = NULL;
}
}
void work() {
cpu->calculate();
vc->display();
memory->store();
}
private:
CPU * cpu;
VideoCard * vc;
Memory * memory;
};
//具体厂商
class IntelCPU : public CPU
{
public:
virtual void calculate() {
cout << "Intel 的CPU工作中..." << endl;
}
};
class IntelVideoCard : public VideoCard
{
public:
virtual void display() {
cout << "Intel 的显卡工作中..." << endl;
}
};
class IntelMemory : public Memory
{
public:
virtual void store() {
cout << "Intel 的内存工作中..." << endl;
}
};
class NvidiaVideoCard : public VideoCard
{
public:
virtual void display() {
cout << "Nvidia 的显卡工作中..." << endl;
}
};
void test01()
{
Computer * cp1 = new Computer(new IntelCPU,
new IntelVideoCard,new IntelMemory);
cp1->work();
delete cp1;
cout << endl;
Computer * cp2 = new Computer(new IntelCPU,
new NvidiaVideoCard, new IntelMemory);
cp2->work();
delete cp2;
}
int main() {
test01();
return 0;
}