C++中的类继承知识点总结1(13章)

一)类继承总结
继承通过使用已有的类(基类)定义新的类(派生类),使得能够根据需要修改编程代码。公有继承建立 is-a关系,这意味着派生类对象也应该是某种基类对象。作为is-a模型的一部分,派生类继承基类的数据成员和大部分方法,但不继承基类的构造函数、析构函数和赋值运算符。派生类可以直接访问基类的公有成员和保护成员,并能够通过基类的公有方法和保护方法访问基类的私有成员。可以在派生类中新增数据成员和方法,还可以将派生类用作基类,来做进一步的开发。每个派生类都必须有自己的构造函数。程序创建派生类对象时,将首先调用基类的构造函数,然后调用派生类的构造函数;程序删除对象时,将首先调用派生类的析构函数,然后调用基类的析构函数。
如果要将类用作基类,则可以将成员声明为保护的,而不是私有的,这样,派生类将可以直接访问这些成员。然而,使用私有成员通常可以减少出现编程问题的可能性。如果希望派生类可以重新定义基类的方法,则可以使用关键字 virtual将它声明为虚的。这样对于通过指针或引用访问的对象,能够根据对象类型来处理,而不是根据引用或指针的类型来处理。具体地说,基类的析构函数通常应当是虚的。可以考虑定义一个 ABC:只定义接口,而不涉及实现。例如,可以定义抽象类Shape,然后使用它派生出具体的形状类,如Circle和Squarc。ABC必须至少包含一个纯虚方法,可以在声明中的分号前面加上=0来声明纯虚方法。

virtual double area()const=0;

不一定非得定义纯虚方法。
对于包含纯虚成员的类(抽象类),不能使用它来创建对象。纯虚方法用于定义派生类的通用接口。
二)知识点(公有继承的考虑因素)
1.is-a 关系
要遵循 is-a关系。如果派生类不是一种特殊的基类,则不要使用公有派生。例如,不应从 Brain 类派生出Programmer 类。如果要指出程序员有大脑,应将Brain 类对象作为Programmer 类的成员。在某些情况下,最好的方法可能是创建包含纯虚函数的抽象数据类,并从它派生出其他的类。请记住,表示 is-a关系的方式之一是,无需进行显式类型转换,基类指针就可以指向派生类对象,基类引用可以引用派生类对象。另外,反过来是行不通的,即不能在不进行显式类型转换的情况下,将派生类指针或引用指向基类对象。这种显式类型转换(向下强制转换)可能有意义,也可能没有,这取决于类声明(参见图13.4)。
2.什么不能被继
承构造函数是不能继承的,也就是说,创建派生类对象时,必须调用派生类的构造函数。然而,派生类构造函数通常使用成员初始化列表语法来调用基类构造函数,以创建派生对象的基类部分。如果派生类构造函数没有使用成员初始化列表语法显式调用基类构造函数,将使用基类的默认构造函数。在继承链中,每个类都可以使用成员初始化列表将信息传递给相邻的基类。C++11新增了一种让您能够继承构造函数的
机制,但默认仍不继承构造函数。
析构函数也是不能继承的。然而,在释放对象时,程序将首先调用派生类的析构函数,然后调用基类的析构函数。如果基类有默认析构函数,编译器将为派生类生成默认析构函数。通常,对于基类,其析构函数应设置为虚的。
赋值运算符是不能继承的,原因很简单。派生类继承的方法的特征标与基类完全相同,但赋值运算符的特征标随类而异,这是因为它包含一个类型为其所属类的形参。赋值运算符确实有一些有趣的特征,下面介绍它们。
3.赋值运算符
如果编译器发现程序将一个对象赋给同一个类的另一个对象,它将自动为这个类提供一个赋值运算符。这个运算符的默认或隐式版本将采用成员赋值,即将原对象的相应成员赋给目标对象的每个成员。然而,如果对象属于派生类,编译器将使用基类赋值运算符来处理派生对象中基类部分的赋值。如果显式地为基类提供了赋值运算符,将使用该运算符。与此相似,如果成员是另一个类的对象,则对于该成员,将使用其所属类的赋值运算符。正如多次提到的,如果类构造函数使用new 来初始化指针,则需要提供一个显式赋值运算符。因为对于派生对象的基类部分,C++将使用基类的赋值运算符,所以不需要为派生类重新定义赋值运算符,除非它添加了需要特别留意的数据成员。例如,basDMA 类显式地定义了赋值,但派生类1ackDMA 使用为它生成的隐式赋值运算符。
然而,如果派生类使用了 new,则必须提供显式赋值运算符。必须给类的每个成员提供赋值运算符,而不仅仅是新成员。HasDMA 类演示了如何完成这项工作:

hasDMA &hasDMA::operator=(const hasDMA &hs)
{if(this == &hs)return *this;baseDMA::operator=(hs);//copy base portiondelete[]style;//prepare for new stylestyle = new charstd::strlen(hs.style)+1];std::strcpy(style,hs.style);return *this;
}

将派生类对象赋给基类对象将会如何呢?(注意,这不同于将基类引用初始化为派生类对象。)请看下面的例子:

Brass blips;//base class
BrassPlus snips("Rafe plosh",91191,3993.19,600.0,0.12);// derived class
blips = snips;//assign derived object to base object

这将使用哪个赋值运算符呢?赋值语句将被转换成左边的对象调用的一个方法:

blips.operator=(snips);

其中左边的对象是Brass对象,因此它将调用 Brass::operator=(const Brass&)函数。is-a关系允许 Brass 引用指向派生类对象,如 Snips。赋值运算符只处理基类成员,所以上述赋值操作将忽略 Snips的 maxLoan成员和其他BrassPlus成员。总之,可以将派生对象赋给基类对象,但这只涉及基类的成员。
相反的操作将如何呢?即可以将基类对象赋给派生类对象吗?请看下面的例子:

Brass gp("Griff Hexbait"212341200);// base class
BrassPlus temp; //derived class
temp =gPi; //possible?

上述赋值语句将被转换为如下所示:

temp.operator=(gp);

左边的对象是 BrassPlus对象,所以它调用 BrassPlus ::operator=(const BrassPlus &)函数。然而,派生类引用不能自动引用基类对象,因此上述代码不能运行,除非有下面的转换构造函数:

BrassPlus(const Brass &)

与 BrassPlus 类的情况相似,转换构造函数可以接受一个类型为基类的参数和其他参数,条件是其他参数有默认值:

BrassPlus(const Brass & ba,double ml=500,double r=0.1);

如果有转换构造函数,程序将通过它根据 gp来创建一个临时 Brass Plus 对象,然后将它用作赋值运算符的参数。
另一种方法是,定义一个用于将基类赋给派生类的赋值运算符:

BrassPlus & BrassPlus::operator=(const Brass)...

该赋值运算符的类型与赋值语句完全匹配,因此无需进行类型转换。总之,问题“是否可以将基类对象赋给派生对象?”的答案是“也许”。如果派生类包含了这样的构造函数,即对将基类对象转换为派生类对象进行了定义,则可以将基类对象赋给派生对象。如果派生类定义了用于将基类对象赋给派生对象的赋值运算符,则也可以这样做。如果上述两个条件都不满足,则不能这样做,除非使用显式强制类型转换。
5.虚方法
设计基类时,必须确定是否将类方法声明为虚的。如果希望派生类能够重新定义方法,则应在基类中将方法定义为虚的,这样可以启用晚期联编(动态联编):如果不希望重新定义方法,则不必将其声明为虚的,这样虽然无法禁止他人重新定义方法,但表达了这样的意思:您不希望它被重新定义。请注意,不适当的代码将阻止动态联编。例如,请看下面的两个函数:

void show(const Brass &rba)
{rba.ViewAcct();cout << endl;
}
void inadequate(Brass ba)
{ba.ViewAcct();cout << endl;
}

第一个函数按引用传递对象,第二个按值传递对象。现在,假设将派生类参数传递给上述两个函数:

BrassPlus buzz("Buzz Parsec"000011114300);
show(buzz);
inadequate(buzz);

show()函数调用使rba参数成为BrassPlus对象buzz的引用,因此,rba.VicwAcct()被解释为 BrassPlus版本,正如应该的那样。但在inadequate()函数中(它是按值传递对象的),ba是Brass(const Brass )构造函数创建的一个 Brass对象(自动向上强制转换使得构造函数参数可以引用一个 BrassPlus 对象)。因此,在 inadequate()中,ba.ViewAcct()是 Brass 版本,所以只有 buzz的 Brass 部分被显示。
6.析构函数
正如前面介绍的,基类的析构函数应当是虚的。这样,当通过指向对象的基类指针或引用来删除派生对象时,程序将首先调用派生类的析构函数,然后调用基类的析构函数,而不仅仅是调用基类的析构函数。
7.友元函数
由于友元函数并非类成员,因此不能继承。然而,您可能希望派生类的友元函数能够使用基类的友元函数。为此,可以通过强制类型转换将,派生类引用或指针转换为基类引用或指针,然后使用转换后的指针或引用来调用基类的友元函数:

ostream &operator<<(ostream & os,const hasDMA & hs)
{//type cast to match operator<<(ostream &,const baseDMA &)os <<(const baseDMA &)hs;os <"Style:"<<hs.style << endl;return os;
}

也可以使用运算符dyamic_cast<>来进行强制类型转换:

0s << dynamic cast<const baseDMA &> (hs):

这是更佳的强制类型转换方式。
8.有关使用基类方法的说明
以公有方式派生的类的对象可以通过多种方式来使用基类的方法。
——派生类对象自动使用继承而来的基类方法,如果派生类没有重新定义该方法。
——派生类的构造函数自动调用基类的构造函数。
——派生类的构造函数自动调用基类的默认构造函数,如果没有在成员初始化列表中指定其他构造函数。
——派生类构造函数显式地调用成员初始化列表中指定的基类构造函数。
——派生类方法可以使用作用域解析运算符来调用公有的和受保护的基类方法。
——派生类的有元函数可以通过强制类型转换,将派生类引用或指针转换为基类引用或指针,然后使用该引用或者指针来调用基类的友元函数

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

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

相关文章

嵌入式轻量级开源操作系统:HeliOS的使用

嵌入式轻量级开源操作系统:HeliOS的使用 &#x1f4cd;项目地址&#xff1a;https://github.com/heliosproj/HeliOS HeliOS项目是一个社区交付的开源项目&#xff0c;用于构建和维护HeliOS嵌入式操作系统&#xff08;OS&#xff09;。HeliOS是一个功能齐全的操作系统&#xff0…

黑马Java面试教程_P9_JVM虚拟机

系列博客目录 文章目录 系列博客目录前言1. JVM组成1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f;3 41.2 什么是程序计数器&#xff1f;3 4总结 1.3 你能给我详细的介绍Java堆吗? 3 4总结 1.4 什么是虚拟机栈 3 4总结 1.6 能不能解释一下方法区&#xff1f; 3…

YOLO11改进-注意力-引入多尺度卷积注意力模块MSCAM

如何在增强特征图的同时降低计算成本&#xff0c;以提升模型性能。基于此&#xff0c;MSCAM 模块采用了多尺度卷积注意力机制&#xff0c;通过 CAB、SAB 和 MSCB 三个子模块协同工作。CAB 利用自适应池化和卷积操作生成通道注意力权重&#xff0c;强调重要通道特征&#xff1b;…

Kafka无锁设计

前言 在分布式消息队列系统中,Kafka 的无锁设计是其高吞吐量和高并发的核心优势之一。通过避免锁的竞争,Kafka 能够在高并发和大规模的生产环境中保持高效的性能。为了更好地理解 Kafka 的无锁设计,我们首先对比传统的队列模型,然后探讨 Kafka 如何通过无锁机制优化生产者…

Refusal in Language Models Is Mediated by a Single Direction

开源代码&#xff1a;https://github.com/andyrdt/refusal_direction Abstract 会话型大语言模型针对指令遵循和安全性进行了微调&#xff0c;从而产生服从良性请求但拒绝有害请求的模型。虽然这种拒绝行为在聊天模型中普遍存在&#xff0c;但其背后的机制仍然知之甚少。在这…

智慧平台数据可视化解决方案(附实践资料下载)

阿里云详解数据可视化大屏构建&#xff0c;内容涉及数据可视化大屏的构建。以下是该文件的核心内容概要&#xff1a; 数据可视化大屏概述&#xff1a; 数据可视化大屏的定义和目的。大屏在企业决策中的作用。 大屏设计原则&#xff1a; 强调用户体验和视觉美感。信息的清晰度和…

基于openlayers 开发vue地图组件

先看效果 主要功能如下&#xff1a; 测量图源更换放大缩小地图添加点hover点数据切换到地图位置&#xff1b;也设定层级2D3D切换&#xff0c;3D为cesium开发&#xff0c;技术交流可以加V&#xff1a;bloxed 地图工具做了插槽&#xff0c;分为toolbar&#xff08;左上角工具…

人工智能ACA(六)--计算机视觉基础

一、计算机视觉概述 1. 计算机视觉定义 人工智能&#xff08;AI&#xff09;的一个重要分支旨在使计算机和系统能够从图像或多维数据中“理解”和“解释”视觉世界通过模拟人类视觉系统&#xff0c;计算机视觉技术能够自动执行诸如识别、分类、检测和跟踪等任务。 2. 计算机…

叉车作业如何确认安全距离——UWB测距防撞系统的应用

叉车在工业环境中运行&#xff0c;常常需要在狭窄的空间内完成货物的搬运和堆垛&#xff0c;这对操作员的技术水平和安全意识提出了极高的要求。传统的叉车作业依赖操作员的经验和视觉判断来确认安全距离&#xff0c;然而这种方式往往存在误差&#xff0c;特别是在视线受阻或光…

基于深度学习(HyperLPR3框架)的中文车牌识别系统-搭建开发环境

本篇内容为搭建开发环境。包括&#xff1a;python开发环境&#xff0c;Qt/C开发环境&#xff0c;以及用到的各个库的安装和配置。 一、Python开发环境搭建与配置 1、下载并安装Anaconda 我没有用最新的版本&#xff0c;安装的是 Anaconda3-2021.05-Windows-x86_64.exe&#…

神经网络-AlexNet

AlexNet是在2012年的ImageNet竞赛后&#xff0c;整理发表的文章&#xff0c;也是对CNN网络的衍生。 网络结构 AlexNet网络结构如下图所示&#xff0c;网络分为了上下两部分&#xff0c;对应两个不同的GPU训练&#xff0c;可以更好的利用GPU算力。只有在特殊的网络层后&#x…

【R语言遥感技术】“R+遥感”的水环境综合评价方法

R语言在遥感领域中是一个强大的工具&#xff0c;它提供了一系列的功能和优势&#xff0c;使得遥感数据的分析和应用更加高效和灵活。以下是R语言在遥感中的具体应用&#xff1a; 数据处理&#xff1a;R语言可以处理和清洗遥感数据&#xff0c;包括数据转换、滤波处理、去噪和数…

硬件模块常使用的外部中断

对于STM32来说&#xff0c;想要获取的信号是外部驱动的很快的突发信号 例1&#xff1a;旋转编码器的输出信号&#xff1a; 可能很久都不会拧它&#xff0c;不需要STM32做任何事情但是一拧它&#xff0c;就会有很多脉冲波形需要STM32接收信号是突发的&#xff0c;STM32不知道什…

TCN-Transformer+LSTM多变量回归预测(Matlab)添加气泡图、散点密度图

TCN-TransformerLSTM多变量回归预测&#xff08;Matlab&#xff09;添加气泡图、散点密度图 目录 TCN-TransformerLSTM多变量回归预测&#xff08;Matlab&#xff09;添加气泡图、散点密度图预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基本介绍 1.双路创新&#xff…

基于AI IDE 打造快速化的游戏LUA脚本的生成系统

前面写了一篇关于使用AI IDE进行C安全开发的博客《使用AI IDE 助力 C 高性能安全开发&#xff01;》&#xff0c; 得到许多同学们的喜欢&#xff0c;今天我们来继续在游戏开发中扩展一下AI的能力&#xff0c;看看能不能给游戏研发团队一些启发。 在游戏研发中&#xff0c;Lua曾…

windows nacos安装配置

GitHub下载压缩包 解压目录&#xff08;注意不要用中文路径&#xff09; 在mysql先创建数据库nacos&#xff0c;再执行sql脚本 配置数据库 #*************** Config Module Related Configurations ***************# ### If use MySQL as datasource: ### Deprecated conf…

Redis 基本全局命令

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 Redis 基本全局命令 收录于专栏[redis] 本专栏旨在分享学习Redis的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 KEYS EXISTS DEL…

qt QZipReader详解

1、概述 QZipReader 是 Qt 中用于从 .zip 文件中读取和提取文件内容的类。它提供了便捷的方法来访问压缩包中的文件和目录&#xff0c;并允许你解压缩单个或多个文件。通过 QZipReader&#xff0c;你可以以编程方式读取 .zip 文件中的内容&#xff0c;并提取它们到目标目录中。…

开发微信小程序的过程与心得

起因 作为家长&#xff0c;我近期参与了学校的护学岗工作。在这个过程中&#xff0c;我发现需要使用水印相机来记录护学活动&#xff0c;但市面上大多数水印相机应用都要求开通会员才能使用完整功能。作为一名程序员&#xff0c;我决定利用自己的技术背景&#xff0c;开发一个…

基于vue-popperjs的二次封装弹窗

前言&#xff1a; 基于vue-popperjs的二次封装代码 <template><!-- 1. :appendToBody"true"是否把位置加到body外层标签上饿了么UI和antD是true&#xff0c;iview和vuetifyjs是false2. trigger属性触发方式&#xff0c;常用hover悬浮触发、clickToOpen鼠标…