当前位置: 首页 > news >正文

C++ 多态

引例:

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
int main()
{test01();
}

这段代码会显示动物在说话,但函数中的本意是想显示小猫在说话。

因为

void DoSpeak(Animal &animal)

的地址在编译阶段已经被绑定了,

如果想执行小猫在说话,那么就要使用动态多态的技术,使函数的地址在运行时绑定。

零、什么是多态?

多态是面向对象编程中的一个概念,指同一个方法或操作可以被不同的对象调用,产生不同的结果。也可以理解为同一个接口,不同的实现方式。

在多态的概念中,通过继承,子类可以重写父类的方法,从而实现多态。例如,一个父类有一个某方法,子类可以继承该父类,并重写该方法,从而实现不同的行为。

多态的好处在于,可以增强代码的灵活性和可扩展性,让代码更加面向对象。

一、满足动态多态的条件:

1.有继承关系

2.子类重写父类的虚函数(重写:函数除了函数体,函数头一模一样)

二 、动态多态的使用:

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

父类中的被重写函数前面加上virtual,变成虚函数。

例如:void DoSpeak(Animal &animal) 中的 Animal &animal

#include<iostream>
using namespace std;
class Animal
{
public:virtual void speak()//虚函数{cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
int main()
{test01();
}

三、多态的原理

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

父类中的speak没有变成虚函数前,父类的大小时1字节,也就是一个空类

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

加了virtual变成虚函数后,父类大小是8字节,多了一个指针。

#include<iostream>
using namespace std;
class Animal
{
public:virtual void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

有虚函数的类会包含一个虚函数指针vfptr

vfptr会指向一个vftable(虚函数表),vftalble中会存放该虚函数的地址。子类中重写虚函数时会将子类的vftable中的地址覆盖为子类中的虚函数地址。

四、多态案例(计算器)

不用多态的版本:

#include<iostream>
using namespace std;
class Calculator
{
public:int getResult(string oper){if(oper=="+")return m_Nums1+m_Nums2;else if(oper=="-")return m_Nums1-m_Nums2;else if(oper=="*")return m_Nums1*m_Nums2;}int m_Nums1;int m_Nums2;
};void test01()
{ Calculator c;c.m_Nums1=10;c.m_Nums2=10;cout<<c.m_Nums1<<"+"<<c.m_Nums2<<"="<<c.getResult("+")<<endl;cout<<c.m_Nums1<<"-"<<c.m_Nums2<<"="<<c.getResult("-")<<endl;cout<<c.m_Nums1<<"*"<<c.m_Nums2<<"="<<c.getResult("*")<<endl;
}
int main()
{test01();
}

如果想要对计算器的操作方式有拓展,需要修改源码。

真正开发中提倡 “开闭原则”

对拓展进行开放,对修改进行关闭

用多态的版本:

#include<iostream>
using namespace std;
class AbstractCalculator
{
public:virtual int getResult(){return 0;}int m_Nums1;int m_Nums2;
};
class AddCalculator:public AbstractCalculator
{
public:int getResult(){return m_Nums1+m_Nums2;}
};
class SubCalculator:public AbstractCalculator
{
public:int getResult()
{return m_Nums1-m_Nums2;
}
};
class MulCalculator:public AbstractCalculator
{
public:int getResult()
{return m_Nums1*m_Nums2;
}
};
void test01()
{ AbstractCalculator *p=new AddCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"+"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;p=new SubCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"*"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;p=new MulCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"*"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;
}
int main()
{test01();
}

 五、纯虚函数与抽象类

六、多态案例(制作饮品)

#include<iostream>
using namespace std;
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:public AbstractDrinking
{
public://煮水virtual void Boil(){cout<<"煮农夫山泉"<<endl;}//冲泡virtual void Brew(){cout<<"冲泡咖啡"<<endl;}//倒入杯中virtual void PourInCup(){cout<<"倒入杯中"<<endl;}//加入辅料virtual void PutSomething(){cout<<"加入糖与牛奶"<<endl;}
};
class tea:public AbstractDrinking
{
public://煮水virtual void Boil()
{cout<<"煮矿泉水"<<endl;
}//冲泡virtual void Brew()
{cout<<"冲茶叶"<<endl;
}//倒入杯中virtual void PourInCup()
{cout<<"倒入杯中"<<endl;
}//加入辅料virtual void PutSomething()
{cout<<"加入枸杞"<<endl;
}
};
void doWork(AbstractDrinking *abs)
{abs->makeDrink();delete abs;
}
void test01()
{doWork(new coffee);cout<<"------------------"<<endl;doWork(new tea);
}
int main()
{test01();
}

七、虚析构与纯虚析构

父类指针在析构时候,不会调用子类中的析构函数,导致子类如果右堆区属性,出现内存泄露

#include<iostream>
using namespace std;
class Animal
{
public:Animal(){cout<<"Animal的构造函数调用"<<endl;}virtual void speak()=0;~Animal(){cout<<"Animal的析构函数调用"<<endl;}
};
class Cat:public Animal
{
public:Cat(string name){cout<<"Cat的构造函数调用"<<endl;m_Name=new string(name);}void speak(){cout<<*m_Name<<"小猫在说话"<<endl;}~Cat(){if(m_Name!=nullptr){cout<<"Cat的析构函数调用"<<endl;delete m_Name;m_Name=nullptr;}}string *m_Name;
};
void test01()
{Animal *animal=new Cat("tom");animal->speak();delete animal;
}
int main()
{test01();
}

在父类的析构函数前加上virtual,就可以解决问题。

纯虚析构:virtual ~Animal()=0;

类外

Animal::~Animal()
{
    cout<<"Animal纯虚析构函数调用"<<endl;
}

纯虚析构必须要有具体的函数实现

#include<iostream>
using namespace std;
class Animal
{
public:Animal(){cout<<"Animal的构造函数调用"<<endl;}virtual void speak()=0;/*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);}void speak(){cout<<*m_Name<<"小猫在说话"<<endl;}~Cat(){if(m_Name!=nullptr){cout<<"Cat的析构函数调用"<<endl;delete m_Name;m_Name=nullptr;}}string *m_Name;
};
void test01()
{Animal *animal=new Cat("tom");animal->speak();delete animal;
}
int main()
{test01();
}

http://www.lryc.cn/news/163965.html

相关文章:

  • LeetCode 之 二分查找
  • 【性能测试】中间件优化
  • 【算法】查找类——二分查找算法
  • Ansible FIle模块,使用Ansible File模块进行文件管理
  • 索尼mp4变成rsv修复案例(ILME-FX3)
  • 抓拍摄像机开关量控制4K高清手机远程看图建筑生长定时缩时相机
  • c++使用http请求-drogon框架
  • 幼儿棒球运动宣传介绍·野球6号位
  • grpc多语言通信之GO和DART
  • 基于FPGA的RGB图像转Ycbcr实现,包括tb测试文件以及MATLAB辅助验证
  • centos 编译安装的php多版本 切换
  • Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例
  • 2023数学建模国赛E题黄河水沙监测数据分析完整代码分析+处理结果+思路文档
  • 玩转Mysql系列 - 第19篇:游标详解
  • 【量化分析】Python 布林线( Bollinger)概念
  • MySQL的权限管理与远程访问
  • 在Qt创建的UI中放一个显示点云的窗口(PCL+QT5)
  • element ui el-table分页多选功能
  • 理解网络通信的基础:OSI七层模型与TCP/IP五层模型
  • Python爬虫-爬取文档内容,如何去掉文档中的表格,并保存正文内容
  • 【使用Cpolar和Qchan搭建自己的个人图床】
  • flutter解决多个类名重名问题
  • 微信小程序 按钮颜色
  • 【云原生】kubectl常用命令大全
  • git pull
  • C++学习之运算符与表达式
  • vue使用谷歌地图实现地点查询
  • 前端该了解的网络知识
  • python3在虚拟环境实用vscode调试错误输出ModuleNotFoundError: No module named ‘django‘解决方法
  • 如何获得一个Oracle 23c免费开发者版