C++入门day5-面向对象编程(终)

C++入门day4-面向对象编程(下)-CSDN博客


本节是我们面向对象内容的最终篇章,不是说我们的C++就学到这里。如果有一些面向对象的基础知识没有讲到,后面会发布在知识点补充专栏,全都是干货满满的。

https://blog.csdn.net/u2396573637/category_12738259.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12738259&sharerefer=PC&sharesource=U2396573637&sharefrom=from_linkicon-default.png?t=O83Ahttps://blog.csdn.net/u2396573637/category_12738259.html?fromshare=blogcolumn&sharetype=blogcolumn&sharerId=12738259&sharerefer=PC&sharesource=U2396573637&sharefrom=from_link好了,废话不多说,我们开始本节的正文:三大特性之一----多态特性


初识:多态特性

多态的基本概念

多态是C++面向对象的三大特性之一。其实早在运算符重载那一章节我们就已经在接触多态了。只不过我们当时还不认识,看完下文你就知道,本节你已经是有基础傍身的“大白”了(反正不是小白)

运算符重载链接:C++入门day3-面向对象编程(中)-CSDN博客

多态分为两种:

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

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

二者的区别:

静态多态:函数地址早绑定,编译时确定

动态多态:函数地址晚绑定,运行时确定

virtual关键字

C++中的virtual关键字主要有这样几种使用场景:第一,修饰父类中的函数 ;第二,修饰继承性。注意:友元函数、构造函数、static静态函数不能用virtual关键字修饰。普通成员函数和析构函数可以用virtual关键字修饰。

virtual具有继承性:父类中定义为virtual的函数在子类中重写的函数也自动成为虚函数。

一定要注意: 只有子类的虚函数和父类的虚函数定义完全一样才被认为是虚函数,比如父类后面加了const,如果子类不加的话就是隐藏了,不是覆盖.

函数重写(覆盖)

定义:子类重新定义父类中有相同名称返回值参数虚函数

class father{
public:virtual void speak(){cout<<"我是父亲"<<endl;}
};
class son:public father{
public:/*virtual*/ void speak(){cout<<"我是儿子"<<endl;}
}

 基本条件:

1.被重写的函数必须为vitual函数,并位于父类中

2.重写的函数与被重写的函数除了函数体可以不一样,其余的函数名、返回值、参数及类型都必须完全一致

如果我们不适用virtual关键字,分别在父类与子类写两个函数:查看son的内存布局

我们看不到任何东西存在。 

我们先在父类函数中加上virtual关键字,如上段代码,然后利用终端查看son类的内存布局情况:

在这里我们看到子类那里只有一个来自父类的虚函数表指针(virtual function-table ptr),而下面还附带一个son域内的虚函数表(virtual function table),里面有son::speak函数名。运行时自动检测是哪个类创建的对象调用的函数,这个过程就是根据虚函数表指针访问虚表然后找到被调函数的函数地址的过程。

当然,我们仍然可以通过加作用域的方式进行子类访问父类函数:

 函数隐藏

1.对于上文,如果父类子类之间有函数的函数名一致,其它不一定一致,那么会发生函数隐藏,此时子类创建的对象会优先匹配子类本身的函数。

2.如果函数要素完全一致:双方都没有virtual修饰,是函数隐藏。

多态案例分析

class father {
public:virtual void speak() {cout << "我是father" << endl;}void work() {cout << "上班" << endl;}
};
class son :public father {
public:void speak() {cout << "我是son" << endl;}void work() {cout << "上学" << endl;}
};
class daughter :public father {
public:void speak() {cout << "我是daughter" << endl;}void work() {cout << "嫁人" << endl;}
};

多态的基础 :需要有重写:子类重写父类的返回值、函数名、参数列表完全一致的虚函数。

(只要父类的函数是虚函数即可)

int main(){father *f1=new son;f1->speak();f1->work();father *f2=new daughter;f2->speak();f2->work();return 0;
}

动态多态:父类指针类型的变量或父类引用类型的变量,使用子类类型进行new创建。(即父指针指向子对象。)并通过该指针或引用调用子类的重写出来的虚函数的现象是动态多态 

动态的过程体现在,函数传参时,只要形参是父类指针或引用类型,那么传入子类时,就会自动使用子类类型的一系列重写的成员函数。其实就有点类似于局限版的模板了。

对于重写的函数,f会调用子类的重写函数,对于隐藏的函数,f会调用父类本身的函数。

小结

总结:

一、多态满足条件:

1.有继承关系

2.子类重写父类虚函数

二、多态使用条件:

父类指针或引用指向子类对象

多态的实现

C++为了实现多态,使用了一种动态绑定的技术,这个技术的核心内容就是虚函数表

虚函数表我们在上文也提到过,在这里我再放一下图大家有个基础的认识:

类的虚函数表

当子类中重写一个或多个父类的虚函数时,这些虚函数不会直接存在类内,而是添加一个数组--虚函数表,数组内存放的是函数的一个个虚函数指针 。

虚函数表:简称虚表

【⚪】虚表是一个存放指针的数组,内部元素是虚函数的指针。普通函数(非虚函数)调用不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。

【⚪】虚表内的条目--即虚函数指针,指针的赋值发生在编译阶段。也就是说在代码的编译阶段,虚表就已经构建出来了 

1. 每个包含了虚函数的类都包含一个虚表(存放虚函数指针的数组)

2.当子类继承父类时,子类会继承父类的函数的调用权。所以说如果一个父类包含了虚函数,那么子类也可以调用这些虚函数,(即上文提到的作用域指定访问)。换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有了自己的虚表。

【⚪】虚表是属于类的,而不是属于某个具体的对象,一个类只有一个虚表,虚表这个数组就相当于static修饰的静态成员一样。同一个类的所有成员都使用同一个虚表。

虚表指针

虚表指针:即上文提到的虚函数表指针,用于访问类的虚表,一定程度上相当于隐藏的静态成员指针

类创建的对象通过虚表指针来访问类的虚表。简单来讲就是将数组的标准形式改为了指针形式。

        为了指定对象的虚表,对象内部包含一个虚表的指针,来指向自己所使用的虚表。为了让每个包含虚表的类的对象都拥有一个虚表指针,编译器在类中添加了一个静态成员指针变量:* vfptr,用来指向虚表,这样,当类的成员在创建时就拥有了这样的指针,且这个指针的值会自动被设置为指向类的虚表。

        验证vfptr指针的方法(_vfptr不可访问),就是用sizeof()先求一个普通类占的字节数大小,然后将类中的某一个函数前使用virtual修饰使其变为虚函数,再求该类占的字节数的大小,会发现多了四个字节,这就验证了vfptr的存在。当然也可以使用终端查看,方法如下

【关于如何使用终端查看类的布局教程-CSDN博客】

动态绑定

动态绑定我们会单独讲解,有需要可以到主页找一找,或者是在知识点补充专栏查找。如果没有找到请等待一两天,博主会加紧把文章码出来的。(专栏链接在文章开头)

纯虚函数与抽象类

纯虚函数

纯虚函数的语法:(当类中有了纯虚函数,这个类也被称为抽象类。)

virtual 返回值类型 函数名 (参数列表) = 0;

抽象类的特点:

1.无法实例化对象

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

Tips:虚函数在虚表中存放的是函数地址,而纯虚函数在虚表中存放的是0。

抽象类(接口)

        接口是为了描述类的行为和功能,不需要完成类的特定实现。C++的接口就是使用抽象类来实现的,抽象类与数据抽象互不混淆,数据抽象是一个把细节与相关数据分离的过程的这样一个概念。

        如果类中至少有一个纯虚函数,那么这个类就称为抽象类。语法同上。

        设计抽象类(Abstract-Class,ABC)的目的是为了给派生类提供一个行为的约束,必须要完成重写这些虚函数的功能才能自行拓展自身特殊行为。抽象类不能被实例化为对象,这一点使得抽象类可以很好的作为接口使用。相对的,非抽象类即为具体类

        抽象类的设计策略:面向对象的系统可能会使用一个抽象基类为所有的外部应用程序提供一个适当的、通用的、标准化的接口。然后,派生类通过继承抽象基类,把所有操作继承下来。

        这种架构具有很强的可拓展性。

虚析构与纯虚析构

回顾析构

在学习虚析构与纯虚析构之前。我们想一下普通的析构能解决什么问题?

防止对象的成员指针被重复释放时导致的无法释放nullptr的问题。

虚析构

虚析构是为了解决父类指针无法释放子类对象的问题的。

我们先来看一段代码:

#include<iostream>
using namespace std;class father abstract{
public:virtual void speak() = 0; virtual void work() = 0;virtual void show() = 0;~father() {cout << "father _destruct" << endl;}
};
class son :public father {
public:void speak() {cout << "我是son" << endl;}void work() {cout << "上学" << endl;}void show() {}~son() {cout << "son _destruct" << endl;}
};int main() {father* fs = new son;delete fs;return 0;
}

乍一看没啥问题,我们看一下运行结果: 

显然,只调用了父类的析构函数,子类的析构函数没有被调用。这就导致子类的资源得不到释放,这就造成了内存泄露的问题。

当我们在父类的析构函数之前加上virtual关键字后:

 

这时候二者的资源都被释放了。这才是我们想看到的结果。

虚析构:virtual ~类名(){}

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

虚析构与纯虚析构的区别:一旦类内有纯虚析构,类就是抽象类了,无法进行实例化了就。

 总结

1.虚析构或纯虚析构就是为了解决父类释放子类对象的问题的

2.如果子类中没有堆区数据,也可以不写虚析构或纯虚析构

3.拥有纯虚析构的类也属于抽象类

共性:都需要具体的函数实现;都可以解决父类指针释放子类对象的问题

区别:有纯虚析构的类是抽象类,无法实例化对象


遗留问题

        我们上节课遗留了一个问题,就是菱形继承问题。什么是菱形继承呢,简单说:你画个菱形,菱形的每个顶点都代表着一个类,其中第一层一个顶点,作为基类,第二层两个顶点,均由基类派生,第三层一个顶点,这个类继承第二层的两个类。继承关系构成了一个菱形,所以我们形象的称之为菱形继承。

菱形继承

上述的情况就是简单的菱形继承,代码如下:

class Animal{
public:int _age;void eat(){cout<<"eat"<<endl;}
};
class Wolf:public Animal{
public:int w_num;void speak(){cout<<"嗷呜~"<<endl;}
};
class Dog:public Animal{
public:int d_num;void speak(){cout<<"汪汪~"<<endl;}
};
class WolfDog:public Wolf,public Dog{
public:int _xxx;void show(){cout<<"我是狼狗"<<endl;}
};

         此时,我们的WolfDog的成员有哪些东西呢?首先wolf类继承了animal的_age属性,dog类也继承了_age属性,那么WolfDog继承Wolf和Dog两类时,同时继承了来自二者的_age属性。这样的话就会导致我们访问_age时,出现访问不明确的问题,我们要通过作用域限制明确访问。

此时,菱形继承带来了一个问题:二义性,还有数据冗余的问题。致使我们使用时非常不方便,因此C++提供了虚拟继承的技术来解决菱形继承带来的问题。 

虚拟继承

根据下图我们可以看出来,它的底层对象模型的布局与我们分析的相一致。这就是为什么普通的菱形继承会带来二义性及数据冗余的问题。

普通菱形继承的底层对象模型

虚拟继承的语法:

class A{};
class B:virtual public A{};

运用虚拟继承:

class Animal {
public:int _age;void eat() {cout << "eat" << endl;}
};
class Wolf :virtual public Animal {
public:int w_num;void speak() {cout << "嗷呜~" << endl;}
};
class Dog :virtual public Animal {
public:int d_num;void speak() {cout << "汪汪~" << endl;}
};
class WolfDog :public Wolf, public Dog {
public:int _xxx;void show() {cout << "我是狼狗" << endl;}
};

 查看底层模型我们可以知道,此时WolfDog类只有一个_age,直接继承来自Animal类的_age,第二层的两个类都是虚拟继承的Animal,可以理解为不算真正拥有。那它们怎么访问_age属性呢,这时候哦我们又看到了一个vbptr和vbtable,之前我们看到的是vfptr和vftable。后面这个我们认识,那这个vb到底是什么意思呢?

vbptr(virtual base-class-table pointer)虚基类表指针

vbtable(virtual base-class table)虚基类表

虚拟菱形继承的底层对象模型 

实际运用时很少用多继承语法,基本上不会遇到菱形继承问题,但需要我们理解底层,有助于你的实力提升。我们大多会使用  组合 的技术,即类内定义对象成员。 


感谢观看,如果有需要互3的小伙伴可以关注+私信,看到必回哦。

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

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

相关文章

阿博图书馆管理:SpringBoot实战指南

第二章 开发技术介绍此次B/S结构、Java技术以及mysql数据库是该阿博图书馆管理系统的主要开发技术&#xff0c;然后对系统的整体设计、数据库设计、功能模块设计、系统页面设计以及系统程序设计进行了详细的研究与规划。 2.1 系统开发平台 在该阿博图书馆管理系统中&#xff0c…

OJ在线评测系统 代码沙箱优化模版方法模式 使用与有规范的流程 并且执行流程可以复用

代码沙箱优化模版方法模式 上次我们代码沙箱的docker实现和原生实现 我们可以使用模版方法设计模式去优化我们的代码 我们原生的java实现代码沙箱和docker实现代码沙箱是有更多重复点的 比如说把文件 收集文件 进行校验 我们要用模版方法设计模式 定义一套通用的执行流程 让…

2024年合肥市职业院校技能大赛(中职组)赛 网络安全理论题

竞赛内容 模块A: 理论技能与职业素养 培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;624032112 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技…

cubemx配置ADC

参考博客&#xff1a;https://blog.csdn.net/qq_29031103/article/details/119894077 生成代码&#xff1b; 接着编写代码&#xff1a; 1&#xff09;在main函数bsp初始化部分&#xff1b; 添加&#xff1a;HAL_ADCEx_Calibration_Start(&hadc1); 2&#xff09;在…

C++之STL—常用拷贝和替换算法

copy(iterator beg, iterator end, iterator dest); // 按值查找元素&#xff0c;找到返回指定位置迭代器&#xff0c;找不到返回结束迭代器位置 // beg 开始迭代器 // end 结束迭代器 // dest 目标起始迭代器 replace(iterator beg, iterator end, oldvalue, newvalue); …

Techub专访顾荣辉教授:解密CertiK的安全战略路线

当 Web3 安全审计公司还在争抢审计份额时&#xff0c;CertiK 已经开始将目光瞄准即将进军 Web3 的传统商业巨头。CertiK 不仅在传统行业进行白帽行动获得如苹果公司的官方感谢&#xff0c;还是 Web3 行业唯一一家拥有 SOC 2 和 ISO 认证的 Web3 的安全公司。基于此&#xff0c;…

Redis篇(数据类型)

目录 讲解一&#xff1a;简介 讲解二&#xff1a;常用 一、String类型 1. 简介 2. 常见命令 3. Key结构 4. 操作String 5. 实例 二、Hash类型 1. 简介 2. 常见命令 3. 3操作hash 4. 实例 三、List类型 1. 简介 2. 特征 3. 应用场景 4. 常见命令 5. 操作list …

AT89C51 利用SBIT寻址,并且在内存中实现伪动态密码的混淆

前置发现 && 分析 char bdata DB[2]; //char sbit x bdata DB[0]^7; //取内存地址数组[0]地址的的七位 这样我们可以对数组DB中索引0的位置进行修改… 例如,将密码A映射到真实密码C,这样做的好处是你的程序被逆向分析的时候,攻击者无法真正知道密码到底是什么…因为…

程序员必备秘籍:掌握代码规范,让开发效率飞速提升!

在软件开发的世界里&#xff0c;代码规范如同指南针&#xff0c;为开发团队和测试团队指明方向。一个统一的代码规范不仅能够提升代码的可读性和可维护性&#xff0c;还能显著提高开发效率和团队协作能力。 本文将带你深入了解代码规范的重要性&#xff0c;并揭示如何通过它来实…

Angular与Vue的全方位对比分析

一、框架概述 Angular Angular是由Google开发和维护的一款开源JavaScript框架。它采用TypeScript编写&#xff0c;具有一套完整的开发工具和规范。Angular遵循MVC&#xff08;Model - View - Controller&#xff09;或更确切地说是MVVM&#xff08;Model - View - ViewModel&a…

探索私有化聊天软件:即时通讯与音视频技术的结合

在数字化转型的浪潮中&#xff0c;企业对于高效、安全、定制化的通讯解决方案的需求日益迫切。鲸信&#xff0c;作为音视频通信技术的佼佼者&#xff0c;凭借其强大的即时通讯与音视频SDK&#xff08;软件开发工具包&#xff09;结合能力&#xff0c;为企业量身打造了私有化聊天…

原生代理IP是什么?

代理IP的各个类型称呼有很多&#xff0c;且它们在网络使用和隐私保护方面扮演着不同的角色。今天将探讨什么是原生IP以及原生IP和住宅IP之间的区别&#xff0c;帮助大家更好地理解这两者的概念和实际应用&#xff0c;并选择适合自己的IP类型。 一、什么是原生IP&#xff1f; 原…

【Transformers实战篇2】练习之命名实体识别

文章目录 一、命名实体识别简介1.1 数据标注体系1.2 IOB2标注体系1.3 IOBES标注体系 二、代码实战2.1 导入相关包2.2 加载数据集2.3 数据集预处理2.3.1 借助word_idx实现标签映射 2.4 创建模型2.5 创建评估函数2.6 配置训练参数2.7 创建训练器2.8 模型训练2.9 模型预测 本文为 …

基于SSM的图书管理管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于SSM的图书管理管理系统4拥有两种角色&#xff0c;用户可以浏览评论图书、登录注册&#xff0c;管理员可以进行图书馆管理、用户管理、分类管理等功能 1.1 背景描述 图书书店销售管理…

Apache OFBiz SSRF漏洞CVE-2024-45507分析

Apache OFBiz介绍 Apache OFBiz 是一个功能丰富的开源电子商务平台&#xff0c;包含完整的商业解决方案&#xff0c;适用于多种行业。它提供了一套全面的服务&#xff0c;包括客户关系管理&#xff08;CRM&#xff09;、企业资源规划&#xff08;ERP&#xff09;、订单管理、产…

Colorful/七彩虹将星X17 AT 22 Win11原厂OEM系统 带COLORFUL一键还原

安装完毕自带原厂驱动和预装软件以及一键恢复功能&#xff0c;自动重建COLORFUL RECOVERY功能&#xff0c;恢复到新机开箱状态。 【格式】&#xff1a;iso 【系统类型】&#xff1a;Windows11 原厂系统下载网址&#xff1a;http://www.bioxt.cn 注意&#xff1a;安装系统会…

这五本大模型书籍,让你从大模型零基础到精通,非常详细收藏我这一篇就够了

大模型&#xff08;Large Language Models, LLMs&#xff09;是近年来人工智能领域的一大热点&#xff0c;它们在自然语言处理、对话系统、内容生成等多个方面展现出了强大的能力。随着技术的发展&#xff0c;市面上出现了许多介绍大模型理论与实践的书籍&#xff0c;为研究人员…

【ADC】SAR 型 ADC 和 ΔΣ ADC 的选型决策方法

本文学习于TI 高精度实验室课程&#xff0c;介绍如何选择 SAR 或 delta-sigma 型 ADC。 文章目录 一、选型决策树二、特定传感器的应用三、需要 DC 精度但分辨率较低的应用四、需要 DC 精度且分辨率较高的应用五、极低噪声的 DC 精密测量六、需要捕获瞬态信号值的应用七、需要高…

敏感字段加密 - 华为OD统一考试(E卷)

2024华为OD机试(E卷+D卷+C卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 【敏感字段加密】给定一个由多个命令字组成的命令字符串: 1、字符串长度小于等于127字节,只包含大小写字母,数字,下划线和偶数个双引号; 2、命令字之间以一个或多个下划线 进行分割; 3、可…

朋友圈信息流广告投放,曝光成本是多少?

微信作为国内最流行的社交平台之一&#xff0c;其朋友圈广告凭借精准的用户画像和强大的社交属性&#xff0c;成为了众多品牌商家进行市场推广的重要渠道。云衔科技推出了专业的微信朋友圈广告开户及代运营服务&#xff0c;旨在帮助企业轻松跨越技术门槛&#xff0c;精准触达目…