所在的位置: C++ >> C++前景 >> C编程语言多态

C编程语言多态

盖博士盖百霖 https://disease.39.net/yldt/bjzkbdfyy/6100089.html

1.多态的基本概念

多态是C++面向对象三大特性之一

多态分为两类

    1.静态多态:函数重载,运算符重载属于静态多态,复用函数名

    2.动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别

    1.静态多态的函数地址早绑定------编译阶段确定函数地址

    2.动态多态的函数地址晚绑定-------运行阶段确定函数地址

动态多态满足条件

1.有继承关系

2.子类重写父类的虚函数

动态多态使用

    父类的指针或引用,执行子类对象

重写:函数返回值类型,函数名,参数列表,完全一致成为重写

动态多态和静态多态的代码演示

//动物类classAnimal{public://虚函数virtualvoidspeak(){cout"动物在说话"endl;}};//猫类classCat:publicAnimal{public:voidspeak(){cout"小猫在说话"endl;}};//狗类classDog:publicAnimal{public:virtualvoidspeak(){cout"小狗在说话"endl;}};//执行说话函数voiddoSpeak(Animalanimal){//Animalanimal=cat引用animal.speak();}voidtest01(){Catcat;doSpeak(cat);//输出为动物在说话,因为地址早绑定,在编译阶段确定函数地址//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定。Dogdog;doSpeak(dog);//在加上virtual后,输出为小狗在说话}2.多态案例01----计算机类类

案例描述:

    分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算机器类

多态的优点

    1.代码组织结构清晰

    2.可读性强

    .利用前期和后期的扩展以及维护

普通的实现

//普通写法classCalculator{public:intgetResult(stringoper){if(oper=="+"){returnm_Num1+m_Num2;}elseif(oper=="-"){returnm_Num1-m_Num2;}elseif(oper=="*"){returnm_Num1*m_Num2;}//如果想扩展新的功能,需要修改源码//在真的开发中提倡开闭原则//开闭原则:对扩展进行开发,对修改进行关闭}intm_Num1;//操作数1intm_Num2;//操作数2};voidtest01(){//创建计算器对象Calculatorc;c.m_Num1=10;c.m_Num2=10;coutc.m_Num1"+"c.m_Num2"="c.getResult("+")endl;coutc.m_Num1"-"c.m_Num2"="c.getResult("-")endl;coutc.m_Num1"*"c.m_Num2"="c.getResult("*")endl;}intmain(){test01();}

多态版本

classAbstractCalcuator{public:virtualintgetResult(){return0;}intm_Num1;intm_Num2;};classAddCalculator:publicAbstractCalcuator{public:intgetResult(){returnm_Num1+m_Num2;}};classSubCalculator:publicAbstractCalcuator{public:intgetResult(){returnm_Num1-m_Num2;}};classMulCalculator:publicAbstractCalcuator{public:intgetResult(){returnm_Num1*m_Num2;}};voidtest02(){//多态使用条件//父类指针或者引用指向子类对象AbstractCalcuator*abc=newAddCalculator;abc-m_Num1=10;abc-m_Num2=10;coutabc-m_Num1"+"abc-m_Num2"="abc-getResult()endl;//用完记的销毁deleteabc;}intmain(){test02();}.纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual返回值类型函数名(参数列表)=0;

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

  1.无法实例化对象

  2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类

//虚函数和抽象类classBase{public://纯虚函数//只要有一个纯虚函数,这个类称为抽象类/**抽象类特点1.无法实例化对象2.子类必须重写抽象类中的纯虚函数,否则也属于抽象类*/virtualvoidfunc()=0;};classSon:publicBase{virtualvoidfunc(){cout"func函数调用"endl;}};voidtest0(){//Baseb;抽象类是无法实例化对象//newBase;抽象类是无法实例化对象//Sons;子类必须重写抽象类中的纯虚函数,否则也属于抽象类抽象类无法实例化对象Base*base=newSon;base-func();}intmain(){test0();system("pause");return0;}4.多态案例02----制作饮品

案例描述:

    制作饮品的大致流程为:煮水—冲泡—倒入杯中—加入辅料

    利用多态技术实现案例,提供抽象制作饮品的基类,提供子类制作咖啡和茶叶

//多态的案例二制作饮品classAbstractDrinking{public://煮水virtualvoidBoli()=0;//冲泡virtualvoidBrew()=0;//倒入杯子virtualvoidPourInCup()=0;//加入辅料virtualvoidPutSomething()=0;//制作饮品voidmakeDrink(){Boli();Brew();PourInCup();PutSomething();}};//制作咖啡classCoffee:publicAbstractDrinking{//煮水virtualvoidBoli(){cout"煮农夫山泉"endl;}//冲泡virtualvoidBrew(){cout"冲泡咖啡"endl;}//倒入杯子virtualvoidPourInCup(){cout"倒入杯中"endl;}//加入辅料virtualvoidPutSomething(){cout"加入糖和牛奶"endl;}};classTea:publicAbstractDrinking{//煮水virtualvoidBoli(){cout"煮山泉水"endl;}//冲泡virtualvoidBrew(){cout"冲泡茶叶"endl;}//倒入杯子virtualvoidPourInCup(){cout"倒入杯中"endl;}//加入辅料virtualvoidPutSomething(){cout"什么都不加"endl;}};//制作饮品函数voiddoWork(AbstractDrinking*abc){abc-makeDrink();deleteabc;}voidtest01(){//制作咖啡//第一种创建和调用AbstractDrinking*c=newCoffee;//(new是堆区数据需要释放)doWork(c);//第二种创建和调用//doWork(newCoffee);//制作茶doWork(newTea);}intmain(){test01();system("pause");return0;}5.虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码。

解决方式:将父类中的析构函数改为虚析构或者纯虚析构。

虚析构和纯虚构共性:

    1.可以解决父类指针释放子类对象

    2.都需要有具体的函数实现

虚析构和纯虚析构区别:

    1.如果是纯虚析构,该类属于抽象类,无法实例化对象。

虚析构语法:

    virtual~类名(){}

纯虚析构语法:

    virtual~类名(){}=0;

classAnimal{public://纯虚函数virtualvoidspeak()=0;Animal(){cout"Animal的构造函数调用"endl;}//第一种解决方法/*利用虚析构可以解决父类指针释放子类对象时不干净问题virtual~Animal(){cout"Animal的析构函数调用"endl;}*///第二种解决方法//纯虚析构,纯虚析构函数需要重写,才可以编译通过。virtual~Animal()=0;};//纯虚析构函数的实现Animal::~Animal(){cout"Animal的纯虚析构函数调用"endl;}classCat:publicAnimal{public:string*m_Name;Cat(stringname){cout"cat的构造函数调用"endl;//目前堆区创建属性了m_Name=newstring(name);}~Cat(){if(m_Name!=NULL){cout"cat的析构函数调用"endl;deletem_Name;m_Name=NULL;}}virtualvoidspeak(){cout*m_Name"小猫会说话"endl;}};voidtest01(){Animal*animal=newCat("汤姆");animal-speak();deleteanimal;}intmain(){test01();/*运行结果Animal的构造函数调用cat的构造函数调用汤姆小猫会说话Animal的析构函数调用没有走cat的析构函数,m_Name指针指向堆区的数据没有释放,造成数据泄露出现这种情况的原因:父类指针在析构时候,不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露解决方法:把父类的析构函数加virtual变成虚析构此时运行结果Animal的构造函数调用cat的构造函数调用汤姆小猫会说话cat的析构函数调用Animal的析构函数调用*/system("pause");return0;}




转载请注明:http://www.aierlanlan.com/cyrz/5596.html