C++:多态(继承)

hello,各位小伙伴,本篇文章跟大家一起学习《C++:多态》,感谢大家对我上一篇的支持,如有什么问题,还请多多指教 !

文章目录

    • :maple_leaf:多态的概念
    • :maple_leaf:继承中的多态
      • 1.:leaves:虚函数表
    • :maple_leaf:多态原理

🍁多态的概念

在 C++ 中,多态性(Polymorphism)是面向对象编程中一个重要的概念,它允许使用统一的接口来操作不同的对象,从而提高代码的灵活性、可维护性和可扩展性。多态性的实现依赖于两种主要机制:编译时多态性(静态多态性)和运行时多态性(动态多态性)

  1. 编译时多态性(静态多态性):
  2. 在 C++ 中,编译时多态性主要通过函数重载和运算符重载来实现。这种多态性是在编译期间根据函数或运算符的参数类型和数量来选择调用的函数版本,称为静态绑定或早期绑定
  3. 例如,函数重载允许在同一个作用域内定义多个函数名相同但参数列表不同的函数,编译器会根据调用时的参数类型来选择正确的函数。如下实现不同类型进行交换代码:
void Swap(int& left, int& right)
{int temp = left;left = right;right = temp;
}void Swap(double& left, double& right)
{double temp = left;left = right;right = temp;
}void Swap(char& left, char& right)
{char temp = left;left = right;right = temp;
}
  1. 运行时多态性(动态多态性):
  2. 运行时多态性是通过虚函数和继承关系来实现的。这种多态性允许在程序运行时根据对象的实际类型来调用对应的函数,称为动态绑定或后期绑定。
  3. 在 C++ 中,通过在基类中将成员函数声明为虚函数(使用 virtual 关键字),允许派生类覆盖(override)这些虚函数。当通过基类指针或引用调用虚函数时,会根据实际指向的对象类型来决定调用哪个函数版本。

下面是一个简单的示例,展示了 C++ 中的运行时多态性:

#include <iostream>
using namespace std;// Base class
class Animal {
public:// Virtual functionvirtual void speak() {cout << "Animal speaks!" << endl;}
};// Derived class overriding the speak() function
class Dog : public Animal {
public:// Override the speak() functionvoid speak() override {cout << "Dog barks!" << endl;}
};// Derived class overriding the speak() function
class Cat : public Animal {
public:// Override the speak() functionvoid speak() override {cout << "Cat meows!" << endl;}
};int main() {Animal *animal;Dog myDog;Cat myCat;// Pointer to Dog objectanimal = &myDog;animal->speak();  // Output: Dog barks!// Pointer to Cat objectanimal = &myCat;animal->speak();  // Output: Cat meows!return 0;
}

在这个示例中:

  1. Animal 类中的 speak() 函数被声明为虚函数。
  2. Dog 和 Cat 类都重写了 speak() 函数,实现了不同的动作。
  3. 在 main() 函数中,通过 Animal 类指针 animal 分别指向 Dog 对象和 Cat 对象,并调用它们的 speak() 函数。虽然指针类型是基类 Animal,但实际上根据指向的对象类型,调用的是对应的虚函数版本,展示了运行时多态性的特性。

通过多态性,C++ 提供了一种灵活且强大的机制,使得程序能够根据对象的实际类型来决定调用哪个函数版本,从而实现代码的重用性和扩展性。

🍁继承中的多态

1.🍃虚函数表

先出一道题,32位机器下,sizeof(Base)答案是多少呢?

class Base
{
public:virtual void Func1(){cout << "Func1()" << endl;}
private:int _b = 1;
};

答案是:8
在这里插入图片描述
可以看到,除了_b成员,还多一个__vfptr放在对象的前面(注意有些平台可能会放到对象的最后面,这个跟平台有关),对象中的这个指针我们叫做虚函数表指针(v代表virtual,f代表function)。

class Base
{
public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};
class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};
int main()
{Base b;Derive d;return 0;
}
  1. 派生类对象d中也有一个虚表指针,d对象由两部分构成,一部分是父类继承下来的成员,虚表指针也就是存在部分的另一部分是自己的成员。
  2. 基类b对象和派生类d对象虚表是不一样的,这里我们发现Func1完成了重写,所以d的虚表中存的是重写的Derive::Func1,所以虚函数的重写也叫作覆盖,覆盖就是指虚表中虚函数的覆盖。重写是语法的叫法,覆盖是原理层的叫法。
  3. 另外Func2继承下来后是虚函数,所以放进了虚表,Func3也继承下来了,但是不是虚函数,所以不会放进虚表。
  4. 虚函数表本质是一个存虚函数指针的指针数组,一般情况这个数组最后面放了一个nullptr。
  5. 总结一下派生类的虚表生成:a.先将基类中的虚表内容拷贝一份到派生类虚表中 b.如果派生类重写了基类中某个虚函数,用派生类自己的虚函数覆盖虚表中基类的虚函数 c.派生类自己新增加的虚函数按其在派生类中的声明次序增加到派生类虚表的最后。
  6. 这里还有一个童鞋们很容易混淆的问题:虚函数存在哪的?虚表存在哪的? 答:虚函数存在虚表,虚表存在对象中。注意上面的回答的错的。但是很多童鞋都是这样深以为然的。注意虚表存的是虚函数指针,不是虚函数,虚函数和普通函数一样的,都是存在代码段的,只是他的指针又存到了虚表中。另外对象中存的不是虚表,存的是虚表指针。

🍁多态原理

在CS2中,不同枪械的机动性不同:

class Gun
{
public:virtual void Mobility(Gun& gun) = 0;
};class AK47 : public Gun
{
public:void Mobility(Gun& gun){cout << "Mobility = " << _Mobility << endl;}
private:int _Mobility = 215;
};class M4A4 : public Gun
{
public:void Mobility(Gun& gun){cout << "Mobility = " << _Mobility << endl;}
private:int _Mobility = 225;
};void Func(Gun& gun)
{gun.Mobility(gun);
}int main()
{AK47 ak47;M4A4 m4a4;Gun& ak = ak47;Gun& m4 = m4a4;Func(ak);Func(m4);return 0;
}

在这里插入图片描述
在这里插入图片描述

  • 多态必须要满足两个条件:
    1.必须通过基类的指针或者引用调用虚函数
    2.被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

举个简单例子解释,如下代码:

class Base
{
public:virtual void Func1(){cout << "Base::Func1()" << endl;}virtual void Func2(){cout << "Base::Func2()" << endl;}void Func3(){cout << "Base::Func3()" << endl;}
private:int _b = 1;
};
class Derive : public Base
{
public:virtual void Func1(){cout << "Derive::Func1()" << endl;}
private:int _d = 2;
};int main()
{Base b;Derive d;Base& s1 = b;Base& s2 = d;s1.Func1();s2.Func1();return 0;
}

解释:Derive继承BaseFunc1进行重写,重写后的虚函数会产生新的地址,放入虚表中Base&引用子类Derive对象,会发生切割,切割部分其实就是继承Base部分,由于DeriveFunc1进行重写,所以s2虚表中的Func1是已经重写的。

图解:
在这里插入图片描述
在这里插入图片描述
你学会了吗?
好啦,本章对于《C++:多态(继承)》的学习就先到这里,如果有什么问题,还请指教指教,希望本篇文章能够对你有所帮助,我们下一篇见!!!

如你喜欢,点点赞就是对我的支持,感谢感谢!!!

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

代码随想录算法训练营第四十八天| 115.不同的子序列、583. 两个字符串的删除操作、 72. 编辑距离

115.不同的子序列 题目链接&#xff1a;115.不同的子序列 文档讲解&#xff1a;代码随想录 状态&#xff1a;不会 思路&#xff1a; dp[i][j] 表示在 s 的前 j 个字符中&#xff0c;t 的前 i 个字符作为子序列出现的次数。 匹配的情况&#xff1a; 1.当 s[j-1] 与 t[i-1] 匹配…

接口测试(3)

接口自动化 # 获取图片验证码import requestsresponse requests.get(url"http://kdtx-test.itheima.net/api/captchaImage")print(response.status_code) print(response.text) import requestsurl "http://kdtx-test.itheima.net/api/login" header_da…

计算机网络之WPAN 和 WLAN

上一篇文章内容&#xff1a;无线局域网 1.WPAN&#xff08;无线个人区域网&#xff09; WPAN 是以个人为中心来使用的无线个人区域网&#xff0c;它实际上就是一个低功率、小范围、低速率和低价格的电缆替代技术。 &#xff08;1&#xff09; 蓝牙系统(Bluetooth) &#…

QT文件生成可执行的exe程序

将qt项目生成可执行的exe程序可按照以下步骤进行&#xff1a; 1、在qt中构建运行生成.exe文件&#xff1b; 2、从自定义的路径中取出exe文件放在一个单独的空文件夹中&#xff08;exe文件在该文件夹中的release文件夹中&#xff09;&#xff1b; 3、从开始程序中搜索qt&#xf…

CTF php RCE(二)

0x04 php伪协议 这种我们是先看到了include才会想到&#xff0c;利用伪协议来外带文件内容&#xff0c;但是有些同学会问&#xff0c;我们怎么知道文件名是哪个&#xff0c;哪个文件名才是正确的&#xff0c;那么这里我们就得靠猜了 include函数 因为 include 是一个特殊的语…

产品原型设计:从概念到实现的完整指南

如果你是一位产品经理&#xff0c;那么你一定会和原型图打交道&#xff0c;产品原型是产品设计方案和底层逻辑的可视化表达&#xff0c;需要完整清晰地表达出产品目的及需求&#xff0c;在整个产品创造的过程中发挥着不可或缺的作用。而对于一些刚入行的产品经理来说&#xff0…

Instruct-GS2GS:通过用户指令编辑 GS 三维场景

Paper: Instruct-GS2GS: Editing 3D Gaussian Splats with Instructions Introduction: https://instruct-gs2gs.github.io/ Code: https://github.com/cvachha/instruct-gs2gs Instruct-GS2GS 复用了 Instruct-NeRF2NeRF 1 的架构&#xff0c;将基于 NeRF 的三维场景编辑方法迁…

4:表单和通用视图

表单和通用视图 1、编写一个简单的表单&#xff08;1&#xff09;更新polls/detail.html文件 使其包含一个html < form > 元素&#xff08;2&#xff09;创建一个Django视图来处理提交的数据&#xff08;3&#xff09;当有人对 Question 进行投票后&#xff0c;vote()视图…

Python实现吃豆人游戏详解(内附完整代码)

一、吃豆人游戏背景 吃豆人是一款由Namco公司在1980年推出的经典街机游戏。游戏的主角是一个黄色的小圆点&#xff0c;它必须在迷宫中吃掉所有的点数&#xff0c;同时避免被四处游荡的幽灵捉到。如果玩家能够吃掉所有的点数&#xff0c;并且成功避开幽灵&#xff0c;就可以进入…

力扣第一题

1. 两数之和 提示 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素在答案里不能重复出现。 你可…

【测试设计】使用jenkins 插件Allure生成自动化测试报告

前言 以前做自动化测试的时候一直用的HTMLTestRunner来生成测试报告&#xff0c;后来也尝试过用Python的PyH模块自己构建测试报告&#xff0c;在后来看到了RobotFramework的测试报告&#xff0c;感觉之前用的测试报告都太简陋&#xff0c;它才是测试报告应该有的样子。也就是在…

射频硅基氮化镓:两个世界的最佳选择

当世界继续努力追求更高速的连接&#xff0c;并要求低延迟和高可靠性时&#xff0c;信息通信技术的能耗继续飙升。这些市场需求不仅将5G带到许多关键应用上&#xff0c;还对能源效率和性能提出了限制。5G网络性能目标对基础半导体器件提出了一系列新的要求&#xff0c;增加了对…

【LeetCode刷题笔记】LeetCode.11.盛最多水的容器

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

CSAPP Lab5- MallocLab

实验目标 本实验需要用c语言实现一个动态的存储分配器&#xff0c;也就是你自己版本的malloc&#xff0c;free&#xff0c;realloc函数。 实验步骤 tar xvf malloclab-handout.tar解压文件 我们需要修改的唯一文件是mm.c&#xff0c;包含如下几个需要实现的函数 int mm_ini…

婴幼儿自闭症的六大警示前兆:家长需警惕的早期信号

婴幼儿自闭症是一种复杂的发育障碍&#xff0c;其早期症状往往较为微妙&#xff0c;但若能及早发现并干预&#xff0c;对孩子的成长至关重要。以下是婴幼儿自闭症的六个主要前兆&#xff1a; 首先&#xff0c;目光呆滞与反应迟钝。婴儿期&#xff0c;孩子应能对外界刺激如声音…

读书笔记-Java并发编程的艺术-第4章(Java并发编程基础)-第3节(线程间通信)

文章目录 4.3 线程间通信4.3.1 volatile和synchronized 关键字4.3.2 等待/通知机制4.3.3 等待/通知的经典范式4.3.4 管道输入 / 输出流4.3.5 Thread.join()的使用4.3.6 ThreadLocal的使用 4.3 线程间通信 线程开始运行&#xff0c;拥有自己的栈空间&#xff0c;就如同一个脚本…

Java字符串(String、字符串拼接、原理)

文章目录 一、String字符串1.1创建方式【直接赋值、new一个对象】1.1.1 使用字符串字面值直接赋值&#xff1a;&#xff08;1&#xff09;字符串字面量创建String对象的转换过程&#xff08;2&#xff09;一些方法&#xff08;3&#xff09;说明 1.1.2 使用new关键字创建字符串…

接口测试(1)

什么是接口测试 路线&#xff1a; 项目介绍 URL&#xff1a;统一资源定位符 HTTP协议 请求URL 请求行 请求方法 post/put 请求行 请求数据类型 content-type json 请求数据 请求体 响应状态码 响应行 响应数据类型 content-type json 响应数据 请响应体 用户名&#xf…

Mybatis Plus 3.X版本的insert填充自增id的IdType.ID_WORKER策略源码分析

总结/朱季谦 某天同事突然问我&#xff0c;你知道Mybatis Plus的insert方法&#xff0c;插入数据后自增id是如何自增的吗&#xff1f; 我愣了一下&#xff0c;脑海里只想到&#xff0c;当在POJO类的id设置一个自增策略后&#xff0c;例如TableId(value "id",type …

Git 命令行快速入门

前言 &#xff08;1&#xff09;新手个人建议使用TortoiseGit这类图形化界面来上手学习。 &#xff08;2&#xff09;如果一定需要用命令行进行操作&#xff0c;可以按照B站&#xff1a;程式与网页开发者必备技能&#xff01;Git 和 GitHub 零基础快速上手&#xff0c;轻松掌握…