【C++】- 继承(继承定义!!基本格式!切片概念!!菱形继承详解!)

news2024/11/28 14:49:04

继承

  • 了解继承
  • 继承的定义
  • 基类和派生类对象赋值转换
  • 继承中的作用域
  • 派生类的默认成员函数
  • 继承和友元
  • 菱形继承和菱形虚拟继承

了解继承

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在
持原有类特性的基础上
进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象
程序设计的层次结构
,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,
承是类设计层次的复用

通俗来说,继承它允许创建基于现有类的新类。并且继承这个机制提供了一种从已有类(称为父类或基类)继承属性和方法到新类(称为子类或派生类)的方式,使得在子类中可以重用、扩展或修改基类的行为

在这里插入图片描述
在这个比喻中:

  • 祖先的绘画技巧代表了父类(基类)中定义的属性和方法。
  • 子孙继承并改进技巧代表了子类通过继承获取父类的能力,并可以添加新的属性和方法或者重写(override)父类的方法来满足新的需求。
  • 每一代人增加的自己的特色代表了面向对象编程中通过继承实现的多态性和扩展性,即同一行为(如绘画)在不同的子类中可以有不同的实现方式。

继承的定义

class Base {
public:
    void baseMethod() {}
};

class Derived : public Base {
public:
    void derivedMethod() {}
};
int main() {
    Derived d;
    d.baseMethod(); // 调用从Base类继承的方法
    d.derivedMethod(); // 调用Derived类自己的方法
    return 0;
}
这里定义了两个类:Base(基类)和Derived(派生类)。派生类Derived通过公有继承(public)的方式继承了基类Base。这意味着Derived类的对象可以访问Base类中的公有成员函数和属性,在主函数中,创建了Derived类的对象d,然后分别调用了baseMethod和derivedMethod方法。由于Derived类继承了Base类,所以d对象能够访问Base类中定义的baseMethod方法,同时也可以访问Derived类中定义的derivedMethod方法。

在上述示例中我们看到了继承可以有不同的继承方式:
在这里插入图片描述
在C++中,有三种继承方式:公有继承、私有继承和保护继承。这些继承方式定义了派生类如何继承基类的成员
在这里插入图片描述
通常情况下,公有继承是最常用和默认的继承方式,因为它能够提供最大的代码复用和功能扩展能力。私有继承和保护继承在一些特定场景下可能会有用,比如实现细节隐藏或实现接口继承。

在这里插入图片描述使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过
最好显示的写出继承方式。

在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

基类和派生类对象赋值转换

在C++中,基类和派生类之间可以进行对象赋值转换,即将一个基类对象赋值给派生类对象或将一个派生类对象赋值给基类对象。不过,这种转换需要注意以下几点:

  1. 基类指针或引用可以指向派生类对象: 基类指针可以指向派生类对象的地址,这意味着通过基类指针可以访问派生类对象中继承自基类的成员。

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类
的指针是指向派生类对象时才是安全的。

  1. 派生类指针或引用不能指向基类对象: 派生类指针或引用只能指向派生类对象,而不能指向基类对象。
  2. 派生类对象可赋值给基类对象: 将派生类对象赋值给基类对象时,只会将派生类对象中从基类继承的部分进行复制。

这里提到一个特殊的概念:切片就是指将派生类对象赋值给基类对象时,只复制了基类部分的成员,而派生类特有的成员被丢失的情况。这种操作也被称为"对象切割"。
在这里插入图片描述4. 基类对象不能赋值给派生类对象: 由于基类对象只包含基类成员,没有派生类成员,因此不能将基类对象直接赋值给派生类对象。

继承中的作用域

与定义普通的类一样,在继承体系中基类和派生类都有独立的作用域。

在继承中,派生类可以隐藏继承自基类的同名成员。当派生类定义了与基类同名的成员函数或成员变量时,派生类的成员会隐藏基类的同名成员,使得在派生类中无法直接访问被隐藏的基类成员,这种行为叫做隐藏。

隐藏的规则如下:

1.如果派生类中定义了与基类同名的成员函数(包括构造函数和析构函数),则基类的同名成员函数被隐藏。

  • 派生类对象调用同名成员函数时,只能访问派生类中定义的该成员函数,无法访问基类中被隐藏的同名成员函数。(需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  • 如果希望访问基类中的同名成员函数,可以使用作用域解析运算符::来明确指定基类的名称,如基类名::成员函数名。

2.如果派生类中定义了与基类同名的成员变量,则基类的同名成员变量被隐藏。

  • 在派生类内部,直接使用同名成员变量时,只能访问派生类中定义的成员变量,无法访问基类中被隐藏的同名成员变量。
  • 如果需要访问基类中的同名成员变量,可以通过基类的公有或保护接口进行访问。

隐藏可以帮助解决基类和派生类之间成员的命名冲突问题,同时也提供了灵活性和可扩展性。通过隐藏基类的同名成员,派生类可以在不破坏基类接口的情况下,定义自己的成员函数和成员变量,实现对基类的功能扩展或改进

派生类的默认成员函数

在C++中,派生类可以自动继承基类的构造函数、析构函数和拷贝构造函数,这些被自动继承的构造函数称为派生类的默认成员函数。

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
    的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

  2. 默认构造函数:当派生类没有显式定义构造函数时,编译器会自动生成一个默认构造函数。默认构造函数会调用基类的默认构造函数,如果基类没有默认构造函数,则必须在派生类的构造函数中显式调用基类的构造函数来初始化基类成员。

  3. 拷贝构造函数:当派生类没有显式定义拷贝构造函数时,编译器会自动生成一个拷贝构造函数。拷贝构造函数会调用基类的拷贝构造函数来复制基类成员,如果基类没有拷贝构造函数,则必须在派生类的拷贝构造函数中显式调用基类的拷贝构造函数来复制基类成员。

  4. 析构函数:当派生类没有显式定义析构函数时,编译器会自动生成一个析构函数。析构函数会调用基类的析构函数来销毁基类成员,如果基类有虚析构函数,则需要在派生类中也定义虚析构函数。

需要注意的是,派生类的默认构造函数、拷贝构造函数和析构函数只会自动继承基类的对应函数,如果需要定义其他类型的构造函数或赋值运算符重载函数,则需要显式定义。

继承和友元

继承和友元之间的关系如下:

  • 派生类可以继承基类的友元关系,即派生类可以访问基类的私有成员。
  • 基类不能继承派生类的友元关系,即基类不能直接访问派生类的私有成员。
  • 友元关系独立于继承关系,定义为友元的函数或类可以不涉及继承关系。

菱形继承和菱形虚拟继承

接下来介绍一种C++继承机制中的一种特殊情况菱形继承,它发生在一个派生类同时继承了两个直接或间接基类,而这两个基类又共同继承了同一个基类。这种继承关系看起来像一个菱形,因此被称为菱形继承。
在这里插入图片描述
在图中,我们定义了一个基类 Person,然后让两个派生类 Student 和Teacher都继承自Person ,最后再以 Assistant 类型的派生类同时继承 Student 和 Teacher 两个类。

由于 Student 和 Teacher 都继承自 Person,所以在 Assistant 中会有两份来自 Person 的成员,可能会导致一些问题。例如,如果 Person类中有一个虚函数A,而 Student 和 Teacher 类都重新实现了这个函数,那么 Assistant 类就会有两个 A 函数,这就会造成二义性问题。

为了解决这个问题,C++ 提供了虚继承的机制。通过在 Student 和 Teacher 对 Person 的继承声明中添加关键字 virtual,可以确保只有一份 Person 的实例被共享,从而解决了菱形继承的问题。例如:

class Person {
public:
  virtual void A() {}
};

class Student : virtual public Person {};

class Teacher : virtual public Person {};

class Assistant : public Student, public Teacher {};

总的来说,菱形继承是 C++ 继承机制中的一种特殊情况,可以通过虚继承来解决。虚继承的原理是通过虚基类表来实现的,虚继承会带来一定的性能开销,但只有在菱形继承的情况下才会使用虚继承。因此,在设计类继承关系时需要格外注意菱形继承问题,避免出现二义性和其他不必要的问题

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

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

相关文章

【lesson10】高并发内存池细节优化

文章目录 大于256KB的大块内存申请问题大于256KB的大块释放申请问题使用定长内存池脱离使用new释放对象时优化为不传对象大小完整版代码Common.hObjectPool.hThreadCache.hThreadCache.cppConcurrentAlloc.hCentralCache.hCentralCache.cppPageCache.hPageCache.cpp 大于256KB的…

centos 7.7 离线安装docker

centos 7.7 离线安装docker Index of linux/static/stable/x86_64/https://download.docker.com/linux/static/stable/x86_64/ 【1】离线下载docker 压缩包上传至 /usr/local 目录,解压缩,并复制到 /usr/bin/ 目录中。 cd /usr/local/tar -zxvf docke…

C++:CSDN云服务器3.0

废话一会儿 终于,CSDN云服务器又更了 事情是这样的: 我的一个室友,知道了我的CSDN云服务器,觉得感兴趣,就让我开发一个3.0版本的。我媜了好久好久,搞出来个怪名堂,刷块刷得不行。他表示这简直…

List的模拟实现 迭代器

———————————————————— list与vector相比,插入、删除等操作实现的成本非常低,如果在C语言阶段熟悉理解过链表,那么现在实现起来list就显得比较简单,可以说操作层面上比vector更简洁,因为list没有扩…

Leetcode—38. 外观数列【中等】

2024每日刷题&#xff08;111&#xff09; Leetcode—38. 外观数列 实现代码 class Solution { public:string countAndSay(int n) {string ans "1";while(--n) {string next;for(int i 0; i < ans.size(); i) {int cnt 1;char c ans[i];while(i 1 < an…

platfrom tree架构下实现3-Wire驱动(DS1302)

目录 概述 1 认识DS1302 1.1 DS1302 硬件电路 1.2 操作DS1302 1.3 注意要点 2 IO引脚位置 3 添加驱动节点 3.1 更新内核.dts 3.2 更新板卡.dtb 4 驱动程序实现 4.1 编写驱动程序 4.2 编写驱动程序的Makefile 4.3 安装驱动程序 5 验证驱动程序 5.1 编写测试程序…

机器学习数据预处理--连续变量分箱

文章目录 原理概念等宽分箱等频分箱聚类分箱有监督分箱 原理概念 连续变量分箱即对连续型字段进行离散化处理&#xff0c;也就是将连续型字段转化为离散型字段。连续字段的离散过程如下所示&#xff1a; 连续变量的离散过程也可以理解为连续变量取值的重新编码过程&#xff0c…

Qt环境搭建+简单程序实现

Qt是什么 Qt是一个跨平台的C图形用户界面应用程序框架。 框架的本质就是一群大佬发明的让菜鸡写出来的代码也也比较规范 也就是限制程序员的自由&#xff0c;让程序员写出来的代码规范。 库和框架有相似性。 库是被程序员调用的&#xff0c;&#xff08;程序员是主体&…

PyTorch 2.2 中文官方教程(二)

在 YouTube 上介绍 PyTorch PyTorch 介绍 - YouTube 系列 原文&#xff1a;pytorch.org/tutorials/beginner/introyt.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 介绍 || 张量 || 自动微分 || 构建模型 || TensorBoard 支持 || 训练模型 || 模型理解 作者&a…

05 - python操作JSON

JSON认识 JSON&#xff0c;一种轻量级的文本数据交换格式&#xff0c;比XML更小更快&#xff0c;更易解析&#xff0c;爬虫经常要获取接口数据&#xff0c;接口数据就是JSON格式的。 格式示例 # 格式1&#xff1a;JSON 对象 {"name": "李嘉图", "a…

pytorch索引和切片

目录 1. 按索引方式取数据2. 以python切片方式取数据3. 指定index取数据4. ...代表除其前后指定维度外的所有维度5. masked_select() 使用掩码选择元素6. take 矩阵打平后选取 1. 按索引方式取数据 a[0,0,2,4] 其中0&#xff0c;0&#xff0c;2&#xff0c;4是索引从0开始 2. …

3.0 Hadoop 概念

本章着重介绍 Hadoop 中的概念和组成部分&#xff0c;属于理论章节。如果你比较着急可以跳过。但作者不建议跳过&#xff0c;因为它与后面的章节息息相关。 Hadoop 整体设计 Hadoop 框架是用于计算机集群大数据处理的框架&#xff0c;所以它必须是一个可以部署在多台计算机上…

灵活应对:策略模式在软件设计中的应用

策略模式是一种行为型设计模式&#xff0c;它允许定义一系列算法&#xff0c;并将每个算法封装起来&#xff0c;使它们可以互换使用。策略模式让算法的变化独立于使用算法的客户端&#xff0c;使得在不修改原有代码的情况下切换或扩展新的算法成为可能。 使用策略模式的场景包…

无人机遥感技术在地质灾害监测应用分析,多旋翼无人机应急救援技术探讨

地质灾害是指在地球的发展演变过程中&#xff0c; 由各种自然地质作用和人类活动所形成的灾害性地质事件。给人民的生命和财产安全带来严重威胁&#xff0c;因此有必要开展地质灾害预测预报、灾害应急和风险区划 遥感技术的快速发展为我们提供了一种获取实时灾害信息的可靠手段…

使用WPS制作三线表

点击边框和底纹点击1、2、3、4并且应用于表格点击确定 再次选中表格点击右键表格属性选择边框和底纹 选中表格第一行右键点击表格属性选择边框和底纹 如果表格中存在虚线

离线数仓-数据治理

目录 一、前言 1.1 数据治理概念 1.2 数据治理目标 1.3 数据治理要解决的问题 1.3.1 合规性 元数据合规性 数据质量合规性 数据安全合规性 1.3.2 成本 存储资源成本 计算资源成本 二、数据仓库发展阶段 2.1 初始期 2.2 扩张期 2.3 缓慢发展期 2.4 变革期 三、…

基于hadoop+spark的大规模日志的一种处理方案

概述: CDN服务平台上有为客户提供访问日志下载的功能,主要是为了满足在给CDN客户提供服务的过程中,要对所有的记录访问日志,按照客户定制的格式化需求以小时为粒度(或者其他任意时间粒度)进行排序、压缩、打包,供客户进行下载,以便进行后续的核对和分析的诉求。而且CDN…

判断和循环 - switch语句和练习

switch语句格式 switch(表达式) {case 值1:语句体1;break;case 值2:语句体2;break;...default:语句体n1;break; }执行流程&#xff1a; 首先计算表达式的值。依次和case后面的值进行比较&#xff0c;如果有对应的值&#xff0c;就会执行相应的语句&#xff0c;在执行的过程中…

vue项目线上页面刷新报404 解决方法

一.修改配置文件 nginx.conf &#xff0c;并重新加载或重启 我的nginx版本是1.9.9 location / {try_files $uri $uri/ /index.html; }原因&#xff1a; 打包后的dist下只有一个 index.html 文件及一些静态资源&#xff0c;这个是因为Vue是单页应用(SPA)&#xff0c;只有一个…

《Python 网络爬虫简易速速上手小册》第6章:Python 爬虫的优化策略(2024 最新版)

文章目录 6.1 提高爬虫的效率6.1.1 重点基础知识讲解6.1.2 重点案例&#xff1a;使用 asyncio 和 aiohttp 实现异步爬虫6.1.3 拓展案例 1&#xff1a;利用 Scrapy 的并发特性6.1.4 拓展案例 2&#xff1a;使用缓存来避免重复请求 6.2 处理大规模数据爬取6.2.1 重点基础知识讲解…