【C++】——多态

文章目录

  • 多态的概念
  • 多态的定义和实现
  • 虚函数
    • 虚函数的重写(覆盖)
      • 虚函数重写的例外
  • override 和 final关键字
  • 重载、重写和重定义(隐藏)
  • 纯虚函数和抽象类
  • 多态的原理
  • 动态绑定和静态绑定

多态的概念

多态就是多种形态,在执行某个行为时,当不同对象去完成时,执行结果不同。
就好比买火车票:普通人买全价票、学生买学生票、儿童买半价票。虽然大家都是人,但是在买票时结果却不同。这就是典型的多态。

多态的定义和实现

  1. 必须通过基类的指针或者引用才能调用虚函数,这是因为只有基类的指针或引用能够同时指向基类和派生类的对象,从而实现多态的效果。
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

举例:

#define   _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
using namespace std;
class Person
{
public:virtual void BuyTicket() // 虚函数{cout << "全价票" << endl;}
};class Child : public Person 
{
public:virtual void BuyTicket() // 虚函数重写{cout << "半价票" << endl;}
};void Fun(Person& p) // 基类的引用
{p.BuyTicket();
}
int main()
{Person p1;Child c1;Fun(p1);Fun(c1);return 0;
}

在这里插入图片描述

虚函数

被virtual修饰的类成员函数才是虚函数。非成员函数不能被virtual修饰。

class Person
{
public:virtual void BuyTicket(){cout << "全价票" << endl;}
};

虚函数的重写(覆盖)

派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

class Person
{
public:virtual void BuyTicket()// 虚函数{cout << "全价票" << endl;}
};class Child : public Person
{
public:virtual void BuyTicket()// 虚函数重写{cout << "半价票" << endl;}
};

举例:

// 派生类 Dog
class Dog : public Animal {
public:void talk() const override {cout << "汪" << endl;}
};// 派生类 Cat
class Cat : public Animal {
public:void talk() const override {cout << "喵" << endl;}
};// 测试函数,用于展示多态
void test_animal_talk(const Animal& animal) {animal.talk();
}int main() {Dog d1;Cat c1;// 使用基类引用调用多态函数test_animal_talk(d1);  // 输出 "汪"test_animal_talk(c1);  // 输出 "喵"return 0;
}

来一道虚函数的题:


// 基类 A
class A {
public:virtual void func(int val = 1) {cout << "A->" << val << endl;}virtual void test() {func();}
};// 派生类 B
class B : public A {
public:void func(int val = 0)  {cout << "B->" << val << endl;}
};int main() {B* p = new B();p->test();delete p; // 释放动态分配的内存return 0;
}

A: A->0 | B: B->1 | C: A->1 | D: B->0 | E: 编译出错 | F: 以上都不正确
我第一次做选的D,后来分析发现了错误:
派生类B并没有对test进行虚函数重写,所以当我使用派生类对象调用test时,如果在派生类里没有,他会去基类里寻找,因为没有进行虚函数重写,所以答案是B。

虚函数重写的例外

  1. 协变
    重写(Override)虚函数时,如果基类函数返回一个指向基类对象的指针或引用,派生类中的重写函数可以返回一个指向派生类对象的指针或引用。这被称为返回类型的协变。(这里的基类对象可以是可以来自自身的继承体系,也可以来源于其他继承体系。)
class A {};
class B :public A{};//不同的继承
class Person 
{
public:virtual A* f() { return new A; }
};
class Student : public Person 
{
public:virtual B* f() //协变{ return new B; }
};// 同一继承
class Person 
{
public:virtual Person* f(){return new Person;}
};
class Student : public Person
{
public:virtual Student* f() //协变{return new Student;}
};
  1. 析构函数的重写
    为什么要重写虚析构函数呢?
    当你有一个基类,并且从这个基类派生出多个子类时,如果基类有一个析构函数(无论是否为虚),而你想通过基类的指针来删除派生类的对象,那么你应该将基类的析构函数声明为虚的。这样做是为了确保当通过基类指针删除派生类对象时,能够调用到派生类的析构函数,从而正确释放派生类特有的资源。

我们在前面学习继承时就知道编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor,所以只要基类的析构函数加了virtual关键字,它就一定会形成重写。

class Person 
{
public://~Person()virtual ~Person() {cout << "~Person()" << endl; }
};
class Student : public Person 
{
public:virtual ~Student() //构成重写{cout << "~Student()" << endl;}
};
int main()
{Person* p1 = new Person;Person* p2 = new Student;delete p1;delete p2;return 0;
}

在这里插入图片描述

override 和 final关键字

override
override 关键字用于在派生类中明确标记一个成员函数是要重写(Override)基类中的虚函数。
好处:

  1. 如果没有构成虚函数的重写在编译时就会报错,能够及时发现错误。
  2. 增强代码可读性,可以让阅读的人清楚的知道该函数是重写了某个基类的函数。
class Person
{
public:virtual	void func() {// ...}
};class Student : public Person
{
public:virtual	void func() override {// ...}
};

final

final 关键字用于防止类被继承或防止类中的虚函数被进一步重写。
好处:

  1. 防止虚函数被重写,确保某些成员函数不会因为继承而意外被改写
  2. 如果一个类被声明为final,它就不能被继承,有助于保护类不被扩展
class Person
{
public:virtual	void func() final{// ...}
};class Student : public Person
{
public:virtual	void func() {// ...}
};

在这里插入图片描述

重载、重写和重定义(隐藏)

在这里插入图片描述

纯虚函数和抽象类

在虚函数的后⾯写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类,抽象类不能实例化出对象。
如果派⽣类继承后不重写纯虚函数,那么派⽣类也是抽象类。纯虚函数某种程度上强制了派⽣类重写虚函数,因为不重写实例化不出对象。

class Person
{
public:virtual	void func() =0{// ...}
};class Student : public Person
{
public:virtual	void func() { // 必须重写,不然也是抽象类,无法实例化// ...}
};

多态的原理

虚函数表
下图中_vfptr就是虚函数表指针,一个包含虚函数的类中最少都有一个虚函数表指针。
因为虚函数的地址要被放到虚函数表中,虚函数表也简称虚表。
在这里插入图片描述

class Person
{
public:virtual void BuyTicket() // 虚函数{cout << "全价票" << endl;}
};class Child : public Person
{
public:virtual void BuyTicket() // 虚函数重写{cout << "半价票" << endl;}
};void fun(Person& p)
{p.BuyTicket();
}
int main()
{Person p1;Child c1;fun(p1);fun(c1);return 0;
}

虚函数表的原理

通过下图我们可以看到,满⾜多态条件后,底层不再是编译时通过调⽤对象确定函数的地址,⽽是运⾏时到指向的对象的虚表中确定对应的虚函数的地址,这样就实现了指针或引⽤指向基类就调⽤基类的虚函数,指向派⽣类就调⽤派⽣类对应的虚函数。在这里插入图片描述

动态绑定和静态绑定

静态绑定
重载就是典型的静态绑定(前期绑定)
对不满⾜多态条件(指针或者引⽤+调⽤虚函数)的函数调⽤是在编译时绑定,也就是编译时确定调⽤
函数的地址,叫做静态绑定。
动态绑定
满⾜多态条件的函数调⽤是在运⾏时绑定,也就是在运⾏时到指向对象的虚函数表中找到调⽤函数
的地址,也就做动态绑定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/144608.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

九章云极交付总监徐阳受邀为第四届中国项目经理大会演讲嘉宾

全国项目经理专业人士年度盛会 北京九章云极科技有限公司基础设施中心交付总监徐阳先生受邀为PMO评论主办的全国项目经理专业人士年度盛会——2024第四届中国项目经理大会演讲嘉宾&#xff0c;演讲议题为“进阶卓越&#xff1a;自我培养备受需求的项目经理的实战策略”。大会将…

靠AI视频在短视频平台接单月入过万,她怎么做到的?AI视频真的来了

大家好&#xff0c;我是画画的小强 相信大家和我一样&#xff0c;从小也有一个导演梦&#xff0c;感谢AI时代&#xff0c;替我完成了这个梦想&#xff0c;如果你想知道如何实现的&#xff0c;今天这篇文章&#xff0c;你一定要看完&#xff01; 从去年11月份起&#xff0c;随…

python函数三:拆包和交换变量值、引用、匿名函数

文章目录 1. 拆包和交换变量值1.1 拆包1.2 交换变量值 2. 引用2.1 了解引用2.1 把引用当作参数传递 3. 匿名函数3.1 lambda语法3.2 lambda的应用3.3 使用使用函数形式来求解某天是该年的第几天&#xff1f; 1. 拆包和交换变量值 1.1 拆包 拆包&#xff1a;把组合形成的元组形…

Fipexide(FPX):植物组织培养中的新兴化学诱导剂AbMole

在植物科学领域&#xff0c;组织培养技术一直是探索植物发育机制和生物技术应用的强大工具。然而&#xff0c;尽管植物生长调节剂如生长素&#xff08;Auxin&#xff09;和细胞分裂素&#xff08;Cytokinin&#xff09;在促进植物愈伤组织形成和再分化中发挥了关键作用&#xf…

数据结构之树(下),你真的懂吗?

数据结构入门学习&#xff08;全是干货&#xff09;——树&#xff08;下&#xff09; 1 堆 (Heap) 1.1 什么是堆 堆 (Heap) 是一种特殊的完全二叉树&#xff0c;分为最大堆和最小堆。 最大堆&#xff1a;每个节点的值都大于或等于其子节点的值&#xff0c;根节点是整个堆的…

一个实用的贴图工具Snipaste

Snipaste贴图工具操作指南 Snipaste 是一个简单但强大的贴图工具&#xff0c;同时也可以执行截屏、标注等功能。 一、安装与启动 下载Snipaste&#xff1a;访问 Snipaste 的官方网站下载合适的安装包。 安装&#xff1a;双击下载的安装包&#xff0c;按照提示完成安装过程。…

简单题88. 合并两个有序数组 (Python)20240920

问题描述&#xff1a; python&#xff1a; class Solution(object):def merge(self, nums1, m, nums2, n):""":type nums1: List[int]:type m: int:type nums2: List[int]:type n: int:rtype: None Do not return anything, modify nums1 in-place instead.&qu…

大模型微调是否具有技术含量?或者说其技术含量究竟有多少?

有句老生常谈的话&#xff1a;一项工作是否具有技术含量取决于你怎么做&#xff0c;这在大模型&#xff08;LLM&#xff09;方向上尤其如此&#xff0c;因为与传统自然语言处理&#xff08;NLP&#xff09;相比&#xff0c;它的上手门槛变得更低了。 我来举些例子&#xff0c;…

基于微信小程序的剧本杀游玩一体化平台

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 基于微信小程序JavaSpringBootVueMySQL的剧…

pdf编辑器免费版哪个好用?8款pdf编辑软件推荐指南,从入门到精通!

在现代数字化办公中&#xff0c;PDF格式以其稳定及兼容性成为了文档分享的首选。然而&#xff0c;处理PDF文件时&#xff0c;您是否曾感到困惑&#xff0c;不知如何进行编辑&#xff1f;无论是添加文本、替换图像&#xff0c;还是压缩文件&#xff0c;找到合适的工具都是关键。…

算法设计与分析(最长公共子序列

目录 最长公共子序列问题描述代码实现输出结果注意事项 小结&#xff1a; 最长公共子序列 最长公共子序列&#xff08;Longest Common Subsequence, LCS&#xff09;问题是计算给定两个序列的最长子序列的长度&#xff0c;这个子序列不要求连续&#xff0c;但需要保持相同的相…

【QML】qml splash 启动界面 实现代码

1. 工程结构 2. 现象 3. 代码 3.1 main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine>int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #…

华为OD机试 - 字符串划分(Python/JS/C/C++ 2024 E卷 100分)

华为OD机试 2024E卷题库疯狂收录中&#xff0c;刷题点这里 专栏导读 本专栏收录于《华为OD机试真题&#xff08;Python/JS/C/C&#xff09;》。 刷的越多&#xff0c;抽中的概率越大&#xff0c;私信哪吒&#xff0c;备注华为OD&#xff0c;加入华为OD刷题交流群&#xff0c;…

C语言 结构体和共用体——典型实例:洗发牌模拟

目录 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如何模拟洗牌&#xff1f; 如何表示52张扑克牌&#xff1f; 如何保存一副扑克牌&#xff1f; 如何发牌&#xff1f; 如何设计主函数&#xff1f; 如…

前端vue-父传子

父传子的话是在components中创建一个子组件MyTest.vue&#xff0c;并且在父组件中先导入(import MyTest from "./components/MyTest")&#xff0c;再注册&#xff08;在expo二default中写上 compnents:{MyTest}&#xff09;&#xff0c;再使用标签&#xff08;<My…

stm32单片机个人学习笔记5(OLED调试工具)

前言 本篇文章属于stm32单片机&#xff08;以下简称单片机&#xff09;的学习笔记&#xff0c;来源于B站教学视频。下面是这位up主的视频链接。本文为个人学习笔记&#xff0c;只能做参考&#xff0c;细节方面建议观看视频&#xff0c;肯定受益匪浅。 STM32入门教程-2023版 细…

指挥中心操作台怎么布局更合理

在现代社会&#xff0c;指挥中心作为应急响应、监控调度与决策支持的核心区域&#xff0c;其操作台的布局合理性直接关系到工作效率与应急反应速度。一个科学合理的操作台布局&#xff0c;不仅能够提升团队协作效率&#xff0c;还能在关键时刻为决策者赢得宝贵时间。那么&#…

【论文阅读】Slim Fly: A Cost Effective Low-Diameter Network Topology 一种经济高效的小直径网络拓扑

文章目录 Slim Fly: A Cost Effective Low-Diameter Network Topology文章总结1. 摘要2. indroduction3. 主要工作 主要思想references Slim Fly: A Cost Effective Low-Diameter Network Topology Slim Fly&#xff1a;一种经济高效的小直径网络拓扑 SC’14 Maciej Besta 苏…

吸尘器制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

吸尘器制造行业&#xff0c;作为传统制造业的重要组成部分&#xff0c;也在积极探索如何通过先进技术实现生产模式的创新升级。5G智能工厂与物联数字孪生平台的融合应用&#xff0c;为吸尘器制造业的数字化转型铺设了一条高速通道&#xff0c;不仅极大地提升生产效率&#xff0…

ubuntu如何进行自动mount硬盘(简易法)

1. 找到你ubuntu的disk工具 2. 选中你要mount的盘 3. 点击那个设置按钮 4. 选择edit mount options 5. disable user session defaults 6, 填写Mount Point就可以了&#xff0c; 最后输入一次密码&#xff0c;重启设备就搞定了