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

C++——多态 上

目录

一、概念

二、多态的定义及实现

三、动态多态的实现条件

四、重写相关

五、构成重写与同名隐藏的函数有什么区别?

六、C++11 中的override 和 final


一、概念

通俗来说,就是多种形态,当完成某个行为时,当不同的对象去完成时会产生不同的状态

比如买票,普通人买票就是全票,学生买票就是半票,而军人就有可能会免票。

比如刷抖音,小红刷到的大多都是穿搭帅哥明星,小黑刷到的大多都是王者吃鸡,小刚刷到的大多都是健身视频,对于同一个行为,不同得人最终刷到的视频内容大相径庭,这就是多态。

二、多态的定义及实现

下面这是之前写过的动物类之间的继承,Animal为基类,Cat和Dog为由Animal继承下来的俩个派生类。

class Animal { public: Animal(const string& name, const string& sex, int age) : _name(name) , _sex(sex) , _age(age) {} void sleep() { cout << _name << "在睡觉~~~" << endl; } void eat() { cout << _name << "吃东西……" << endl; } protected: string _name; string _sex; int _age; }; class Dog : public Animal { public: Dog(const string& name, const string& sex, int age, const string& color) : Animal(name, sex, age) , _color(color) {} private: string _color; }; class Cat : public Animal { public: Cat(const string& name, const string& sex, int age, const string& Tempter) : Animal(name, sex, age) , _Tempter(Tempter) {} private: string _Tempter; }; void Test1() { Dog dog("小黑", "公", 2, "金黄色"); dog.eat(); dog.sleep(); Cat cat("咪咪", "母", 1, "白色"); cat.eat(); cat.sleep(); } int main() { Test1(); return 0; }

代码的执行中,cat和dog最终的sleep与eat都是打印的同一个结果,因为他们都是继承自基类,都使用的是基类Animal中的同一个成员函数,猫和狗二者的习性是大相径庭的,所以不应该这样笼统的让它们进行同样的行为。

所以给Cat类与Dog类中添加了各自的成员函数,由于派生类中与基类都出现了相同名字的成员,则触发被动——同名隐藏,因此即会使用派生类中的成员函数。

Dog: void eat() { cout << _name << "吃的声音很大" << endl; } void sleep() { cout << _name << "呼噜噜……" << endl; } Cat: void eat() { cout << _name << "悄咪咪的吃东西" << endl; } void sleep() { cout << _name << "zzz……" << endl; }

功能是实现了,可是在与用户接触的Test函数中却书写了大量重复的代码

使用多态

第一步 使用基类引用或指针给子类赋值

void Dynamic(Animal& s) { s.eat(); s.sleep(); } int main() { Dog dog("小黑", "公", 2, "金黄色"); Dynamic(dog); Cat cat("咪咪", "母", 1, "白色"); Dynamic(cat); return 0; }

第二步 将基类Animal中成员函数声明为虚函数

virtual void sleep() { cout << _name << "在睡觉~~~" << endl; } virtual void eat() { cout << _name << "吃东西……" << endl; }

完成!!!

需求:实现一个绘图软件

如果是圆,画圆

如果是矩形,画矩形

如果是三角形,画三角形

class Shape { public: virtual void Drow() { cout << "图形未知,无法作图" << endl; } virtual double GetPerimeter() { cout << "图形未知" << endl; return 0; } }; class Circle : public Shape { public: Circle(double r) : _r(r) {} virtual void Drow() { cout << "○" << endl; } virtual double GetPerimeter() { return 2 * 3.14 * _r; } private: double _r; }; class Rectangle : public Shape { public: Rectangle(double length, double width) : _length(length) , _width(width) { } virtual void Drow() { cout << "□" << endl; } virtual double GetPerimeter() { return 2 * (_length+_width); } private: double _length; double _width; }; class Trangle : public Shape { public: Trangle(double a, double b, double c) : _a(a) , _b(b) , _c(c) {} virtual void Drow() { cout << "△" << endl; } virtual double GetPerimeter() { return _a+_b+_c; } private: double _a; double _b; double _c; }; void Test(Shape& s) { s.Drow(); cout << s.GetPerimeter() << endl; } int main() { Circle c(2.2); Rectangle r(1, 2); Trangle t(3, 4, 5); Test(c); Test(r); Test(t); return 0; }

三、动态多态的实现条件

1)必须在继承前提下,子类必须重写基类的虚函数(被virtual修饰的函数即为虚函数)

2)关于虚函数的调用,通过引用或者指针

3)动态:即只有当程序运行的时候根据传入的对象编译器才会选择类中对应的虚函数进行调用

如果类中哪个方法想要实现多态的效果,则该方法必须为虚函数,并且在子类中必须要被重写

四、重写相关

1)基类中要被重写的成员函数必须为虚函数

2)子类虚函数与基类虚函数的原型要一致(返回值类型、函数名、参数列表必须完全一致)

特例:

① 析构函数一般定义为虚函数(基类与子类函数名不同但构成重写)

② 协变:基类函数返回基类的指针或引用,子类返回子类的指针或引用

返回值类型不同,但也构成重写)

3)子类中的virtual关键字有没有均可(推荐加上)

4)基类与子类的虚函数访问权限可以不同(一般保持一致)

class B { public: virtual ~B() //特例 ① virtual修饰基类的析构函数 { cout << "B::~B()" << endl; } virtual B* GetObjPtr() //特例 ② 基类返回基类的指针,子类返回子类的指针 { return this; } }; class D : public B { public: ~D() { cout << "D::~D()" << endl; } virtual D* GetObjPtr() { return this; } }; void Test(B* t) { delete t; t = nullptr; } void TestAddr(B* t) { if (t) { t->GetObjPtr(); } } int main() { B *pb = new B; Test(pb); D* pd = new D; Test(pd); /////////////////////////////////// B b; TestAddr(&b); D d; TestAddr(&d); return 0; }

五、构成重写与同名隐藏的函数有什么区别?

1)相同点:

1、都在同一个继承体系下,一个在基类中,一个在子类中

2、二者的函数名相同

2)不同点:

1、重写中基类的函数必须是虚函数,而同名隐藏没有要求

2、重写要求俩个函数的原型必须一致(析构函数与协变除外)

而同名隐藏只要求俩个函数函数名相同

下列代码只有func1构成重写

class B { public: virtual void func1() { cout << "B::func1" << endl; } void func2() { cout << "B::func2" << endl; } void func3() { cout << "B::func3" << endl; } void func4(int a) { cout << "B::func3" << endl; } }; class D : public B { public: virtual void func1() { cout << "D::func1" << endl; } void func2() { cout << "D::func2" << endl; } virtual void func3() // 子类中为虚函数 基类中不为虚函数 (不构成重写) { cout << "D::func3" << endl; } virtual void func4(int a) // 子类中为虚函数 基类中不为虚函数 (不构成重写) { cout << "D::func4" << endl; } };

六、C++11 中的override 和 final

从上面可以看出,C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数
名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有
得到预期结果才来debug会得不偿失,因此:C++11提供了override和final两个关键字,可以帮
助用户检测是否重写

final修饰类,表示该类不能被继承。

1)final修饰虚函数,表示该虚函数不能再被重写(最后一个了嘛)

2)override只能修饰子类中的虚函数指定该虚函数必须要实现重写基类中函数(如果无法实现则报错)

http://www.jsqmd.com/news/802082/

相关文章:

  • Transformer如何实现端到端视频重建:工业级落地关键技术解析
  • 2026年国内LD单梁行吊生产商最新推荐排行揭晓 - 企业推荐官【官方】
  • 在 Node.js 后端服务中集成 Taotoken 实现智能客服回复功能
  • Flash+IceVision构建CT新冠病灶检测系统
  • 轻量级AI模型Gemma与MoE架构:低成本部署与高效推理实践指南
  • Dart - 异步编程引入、Future、Future 链式调用
  • RadiAnt DICOM Viewer 2024:解锁高效医学影像工作流的新利器
  • Vivado里配置RFSoC数据转换器IP,这10个参数新手最容易搞错(附PG269避坑指南)
  • 从西门子模块到TI方案:解析热电偶与PT100热电阻采样电路的设计精要与噪声对策
  • 终极iOS设备降级指南:5步让旧iPhone/iPad重获新生 [特殊字符]
  • openclaw官网中文版入口_本地免费部署直连手机教程!
  • 告别手动下载!3步轻松批量获取网易云音乐FLAC无损音乐
  • java在Windows环境下执行cmd命令踩坑记录
  • 告别Rviz!只用Gazebo完成MoveIt机械臂运动规划与仿真的完整工作流
  • 医学影像AI泛化难题:数据偏差与标签噪声如何影响模型临床可靠性
  • Python PyPDF2实战:给你的PDF文件加把‘锁’(加密)和换个‘身份证’(修改元数据)
  • 5步掌握ComfyUI插件:AI图像增强终极指南
  • 从WM8988切换到立晶CL1026?这些PCB布局细节千万别照搬(实战经验分享)
  • 告别调参焦虑!用Matlab Regression Learner App快速搞定你的第一个回归模型(附三维曲面拟合实战)
  • 从2D到3D的思维转换:用AD17给老PCB库“一键升级”,让嘉立创下单前看清每个角落
  • AI时代如何构建个人核心竞争力:从技能清单到能力叙事的实践指南
  • AI技能跨平台迁移实战:从Claude Code到OpenClaw的自动化转换
  • 2026年5月四川钢结构加工优质厂家推荐:螺纹钢等现货直供+配套加工 - 深度智识库
  • ComfyUI Impact Pack 终极指南:释放AI图像细节增强的完整潜能
  • 2026年西安印刷厂一站式服务深度横评:如何找到靠谱的高品质画册定制与活页环装工厂 - 精选优质企业推荐官
  • 学术研究者的终极文档处理方案:ScienceDecrypting 完整指南
  • 2026年4月市面上可靠的3D打印丝源头工厂口碑推荐,三色3D打印增材生产线/3D打印生产线,3D打印丝品牌哪家专业 - 品牌推荐师
  • Windows 11任务栏拖放功能修复:技术原理与高效恢复方案
  • ChatmemoryRedis 历史对话存【REDIS】20260512
  • 开发转产品的334天 260331