前言:多态是C++面向对象三大特性之一。
多态,指的是一个类实例的相同方法在不同情形有不同表现形式。具有不同内部结构的对象可以共享外部接口。C++多态就是用一个更通用的基类指针指向不同的子类实例,为了能调用正确的方法,我们需要用到虚函数和虚继承。
#include using namespace std;class Animal{public: // Speak函数就是虚函数 // 函数前面加上 virtual 关键字变成虚函数,那么编译器在编译的时候就不能确定函数的调用了 virtual void speak(){ cout << "动物在说话" << endl; }};class Cat : public Animal{public: void speak(){ cout << "小猫在说话" << endl; }};class Dog : public Animal{public: void speak(){ cout << "小狗在说话" << endl; }};// 希望传入什么对象,那么就调用什么对象的函数// 如果函数地址在编译阶段就能确定,那么静态联编// 如果函数地址在运行阶段才能确定,就是动态联编void DoSpeak(Animal & animal){ animal.speak(); }// 多态满足条件:// 1、有继承关系// 2、子类重写父类中的虚函数// 多态使用:// 父类指针或引用指向子类对象void test(){ Cat cat; DoSpeak(cat); Dog dog; DoSpeak(dog);}int main(){ test(); return 0;}
多态案例——计算器类
案例描述:分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类。
#include#includeusing namespace std;class Calculator{public: int getResult(string oper){ if(oper == "+"){ return m_Num1 + m_Num2; } else if(oper == "-"){ return m_Num1 - m_Num2; } else if (oper == "*"){ return m_Num1 * m_Num2; } // 如果要提供新的运算,需要修改源码 } int m_Num1; int m_Num2;};void test(){ // 普通实现测试 Calculator c; c.m_Num1 = 10; c.m_Num2 = 20; cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.m_Num1 + c.m_Num2 << endl; cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.m_Num1 - c.m_Num2 << endl; cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.m_Num1 * c.m_Num2 << endl;}// 多态实现// 抽象计算器类// 多态优点:代码组织结构清晰,可读性强,利于前期和后期的扩展以及维护class AbstractCalculator{public: virtual int getResult(){ return 0;} int m_Num1; int m_Num2;};// 加法计算器class AddCalculator : public AbstractCalculator{public: int getResult(){ return m_Num1 + m_Num2; }};// 减法计算器class SubCalculator : public AbstractCalculator{public: int getResult(){ return m_Num1 - m_Num2; }};// 乘法计算器class MulCalculator : public AbstractCalculator{public: int getResult(){ return m_Num1 * m_Num2; }};void test2(){ // 创建加法计算器 AbstractCalculator *abc = new AddCalculator; abc->m_Num1 = 10; abc->m_Num2 = 20; cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; // 用完了销毁 // 创建减法计算器 abc = new SubCalculator; abc->m_Num1 = 10; abc->m_Num2 = 20; cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc; // 创建乘法计算器 abc = new MulCalculator; abc->m_Num1 = 10; abc->m_Num2 = 20; cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl; delete abc;}int main(){ test(); test2(); return 0;}
总结:C++开发提倡利用多态设计程序架构,因为多态优点很多。
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现没有意义,主要都是调用子类重写的内容。因此可以将虚函数改为纯虚函数。
#includeusing namespace std;class Base{public: // 纯虚函数 // 类中只要有一个纯虚函数就称为抽象类 // 抽象类无法实例化对象 // 子类必须重写父类中的纯虚函数,否则也属于抽象类 virtual void func() = 0;};class Son : public Base{public: virtual void func(){ cout << "func调用" << endl; }};void test(){ Base * base = NULL;// base = new Base; // 抽象类无法实例化对象 base = new Son; base->func(); delete base;}int main(){ test(); return 0;}
多态案例二—制作饮品
案例描述:制作饮品的大致流程为:煮水 - 冲泡 - 倒入杯中 - 加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作茶水和咖啡。
#includeusing namespace std;class AbstractDringking{public: // 烧水 virtual void Boil() = 0; // 冲泡 virtual void Brew() = 0; // 倒入杯中 virtual void PourInCup() = 0; // 加入辅料 virtual void PutSomething() = 0; // 规定流程 void MakeDrink(){ Boil(); Brew(); PourInCup(); PutSomething(); }};// 制作茶水class Tea : public AbstractDringking{public: void Boil(){ cout << "煮农夫山泉" << endl; } void Brew(){ cout << "冲泡茶叶" << endl; } void PourInCup(){ cout << "将茶水倒入杯中" << endl; } void PutSomething(){ cout << "加入枸杞" << endl; }};// 制作咖啡class Coffee : public AbstractDringking{public: void Boil(){ cout << "煮纯净水" << endl; } void Brew(){ cout << "冲泡咖啡" << endl; } void PourInCup(){ cout << "将咖啡倒入杯中" << endl; } void PutSomething(){ cout << "加入牛奶" << endl; }};// 业务函数void Dowork(AbstractDringking* drink){ drink->MakeDrink(); delete drink;}void test(){ Dowork(new Tea); cout << "----------------------" << endl; Dowork(new Coffee);}int main(){ test(); return 0;}
虚析构和纯虚析构
多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。
解决方式:将父类中的析构函数改为虚析构或者纯虚析构。
#includeusing namespace std;class Animal{public: Animal(){ cout << "Animal构造函数" << endl; } virtual void Speak() = 0; // 析构函数加上virtual关键字,变成虚析构函数// virtual ~Animal(){// cout << "Animal虚析构函数" << endl;// } virtual ~Animal() = 0;};Animal::~Animal() { cout << "Animal 纯虚析构函数" << endl; }// 和包含普通纯虚函数的类一样,包含类普通纯虚析构函数的类也是一个抽象类。不能够被实例化。class Cat : public Animal{public: Cat(string name){ cout << "Cat构造函数" << endl; m_Name = new string(name); } virtual void Speak(){ cout << *m_Name << "小猫在说话" << endl; } ~Cat(){ cout << "Cat析构函数" << endl; if(this->m_Name != NULL) delete m_Name; m_Name = NULL; } string *m_Name;};void test(){ Animal *animal = new Cat("Tom"); animal->Speak(); // 通过父类指针去释放,会导致子类对象可能清理不干净,造成内存泄露 // 怎么解决?给基类增加一个虚析构函数 // 虚析构函数就是用来解决父类指针释放子类对象 delete animal;}int main(){ test(); return 0;}
总结:
1、虚析构或纯虚析构就是用来解决通过父类指针释放子类对象
2、如果子类中没有堆区数据,可以不写为虚析构或纯虚析构
3、拥有纯虚析构函数的类也属于抽象类
多态案例三 - 电脑组装
案例描述:电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)
将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商。
创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口。
#includeusing namespace std;// 抽象CPU类class CPU{public: // 抽象的计算函数 virtual void calculate() = 0;};// 抽象显卡类class VideoCard{public: virtual void display() = 0;};// 抽象内存条类class Memory{public: virtual void storage() = 0;};class Computer{public: Computer(CPU * cpu, VideoCard * vc, Memory * men){ m_cpu = cpu; m_vc = vc; m_men = men; } // 提供工作的函数 void work(){ // 让零件工作起来,调用接口 m_cpu->calculate(); m_vc->display(); m_men->storage(); } // 提供析构函数,释放三个电脑零件 ~Computer(){ if(m_cpu != NULL){ delete m_cpu; m_cpu = NULL; } if(m_vc != NULL){ delete m_vc; m_vc = NULL; } if(m_men != NULL){ delete m_men; m_men = NULL; } }private: CPU * m_cpu; VideoCard * m_vc; Memory * m_men;};// 具体厂商// Intel厂商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 storage() { cout << "Intel的内存条开始存储了" << endl; }};// Lenovo厂商class LenovoCPU : public CPU{public: virtual void calculate(){ cout << "Lenovo的CPU开始计算" << endl; }};class LenovoVideoCard : public VideoCard{public: virtual void display(){ cout << "Lenovo的显卡开始显示了" << endl; }};class LenovoMemory : public Memory{public: virtual void storage(){ cout << "Lenovo的内存条开始存储了" << endl; }};void test(){ // 第一条电脑零件 CPU * intelCpu = new IntelCPU; VideoCard * intelCard = new IntelVideoCard; Memory * intelMem = new IntelMemory; cout << "Intel零件组装电脑开始工作:" << endl; // 创建第一台电脑 Computer * computer1 = new Computer(intelCpu, intelCard, intelMem); computer1->work(); delete computer1; cout << "------------------------" << endl; cout << "Lenovo零件组装电脑开始工作:" << endl; // 组装第二台电脑 Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory); computer2->work(); delete computer2; cout << "------------------------" << endl; cout << "混装零件电脑开始工作:" << endl; Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory); computer3->work(); delete computer3;}int main(){ test(); return 0;}
- END -