C++:继承~派生类以及衍生的多继承与菱形继承问题

C++中的继承其实是一个C++中的坑,主要体现在其多继承(菱形继承)方面,我们先来了解下继承的概念

一,继承的概念与定义

1.1继承的概念 

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

比如说我们来看下面两个例子:

class Student
{
public:
    // 进入校园/图书馆/实验室刷二维码等身份认证
    void identity()
    {
        // ...
    }
    // 学习
    void study()
    {
        // ...
    }
protected:
    string _name = "peter"; // 姓名
    string _address; // 地址
    string _tel; // 电话
    int _age = 18; // 年龄
    int _stuid; // 学号
};

class Teacher
{
public:
    // 进入校园/图书馆/实验室刷二维码等身份认证
    void identity()
    {
        // ...
    }
    // 授课
    void teaching()
    {
        //...
    }
protected:
    string _name = "张三"; // 姓名
    int _age = 18; // 年龄
    string _address; // 地址
    string _tel; // 电话
    string _title; // 职称
};

 在这两个例子中,老师和学生的两个类间有许多成员是重复的-这样如果数据特别多的时候,就会造成数据的冗余,而继承便可以很好的帮助我们解决这个问题:

class Person
{
public:
    // 进入校园/图书馆/实验室刷二维码等身份认证
    void identity()
    {
        cout << "void identity()" << _name << endl;
    }
protected:
    string _name = "张三"; // 姓名
    string _address; // 地址
    string _tel; // 电话
    int _age = 18; // 年龄
};
class Student : public Person
{
public:
    // 学习
    void study()
    {
        // ...
    }
protected:
    int _stuid; // 学号
};
class Teacher : public Person
{
public:
    // 授课
    void teaching()
    {
        //...
    }
protected:
    string title; // 职称
};

1.2继承的定义 

1.2.1定义格式 

继承方式分为公有继承(public),保护继承(protected),私有继承(private).

1.2.2继承成员访问方式的变化 

 

基类的所有private对于派生类全部不可见,而对于继承下来的其他基类成员,它们则属于派生类中的min(继承方式,基类成员类型) 的成员(public > protected > private),我们一般使用的较多的是public继承,日常使用protected,private继承会降低代码的可维护性

TIPS(补充):设置非继承类与继承方式实现stack

如果我们想要设置一个无法被继承的类,一种方法是直接将基类的所有成员都设置为私有,还有一种是在基类之后加上final,表示这是一个最终类,无法被继承:

class Person final
{
public:
    // 进入校园/图书馆/实验室刷二维码等身份认证
    void identity()
    {
        cout << "void identity()" << _name << endl;
    }
protected:
    string _name = "张三"; // 姓名
    string _address; // 地址
    string _tel; // 电话
    int _age = 18; // 年龄
};

当然,由于我们stl中的常用容器比如vector,list等都是类,所以我们也可以通过继承模板类的方式去模拟实现一个stack函数:

// stack和vector的关系,既符合is-a,也符合has-a
template<class T>
class stack : public std::vector<T>
{
public:
    void push(const T& x)
    {
        // 基类是类模板时,需要指定一下类域,
        // 否则编译报错:error C3861: “push_back”: 找不到标识符
        // 因为stack<int>实例化时,也实例化vector<int>了
        // 但是模版是按需实例化,push_back等成员函数未实例化,所以找不到
        vector<T>::push_back(x);
        //push_back(x);
    }
    void pop()
    {
        vector<T>::pop_back();
    }
    const T& top()
    {
        return vector<T>::back();
    }
    bool empty()
    {
        return vector<T>::empty();
    }
};

二,基类(父类)与派生类(子类)之间的转换 

我们之前的时候介绍过,对于需要强制类型转换的数据,会产生一个中间变量(常量)去进行转换,如果我们不是常值引用,编译器就会报错,然而,对于基类与派生类之间的转换,不会产生中间变量,派生类指针或引用无法指向基类对象,而基类指针或引用则可以指向派生类对象,这是通过裁剪实现的:

此时指针指向的是student的头部,不过会将派生类中的_No成员裁剪,其实这种转换关系也很好理解-因为派生类会包含基类的所有成员,故可以通过裁剪方式进行转换,然而父类中却不一定包含所有的子类成员,因此无法转换

三,继承中的作用域与默认成员函数

3.1隐藏规则 

1. 在继承体系中基类和派生类都有独立的作用域。
2. 派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。
(在派生类成员函数中,可以使用 基类::基类成员 显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 注意在实际中在继承体系里面最好不要定义同名的成员。
 

3.2四个常见默认成员函数

1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
比特就业课
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。需要注意的是派生类的
operator=隐藏了基类的operator=,所以显示调用基类的operator=,需要指定基类作用域
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 因为多态中一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个属于多态部分)。那么编译器会对析构函数名进行特殊处理,处理成destructor(),所以基类析构函数不加
virtual(菱形继承部分我们会解释)的情况下,派生类析构函数和基类析构函数构成隐藏关系。
 

 

所以如果默认的父类构造函数已经够用时,我们便不需要在写基类的构造函数时去调用父类构造函数,如果不够用,则需要以以下方式进行初始化:

class Student : public Person
{
public:
    Student(int stuid)
        :_stuid(stuid)
        , Person()//根据父类的构造函数去输入值
    {}
protected:
    int _stuid;
};

TIPS(补充):继承与友元 

父类的友元无法被子类继承,就好比父亲的朋友不一定是儿子的朋友一样一个道理. 

TIPS(补充):继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都只有一个static成员实例。 也就是说子类中的静态成员与父类中的静态成员是一个成员

四,多继承与菱形继承 

4.1继承模型

1.单继承:一个派生类只有一个直接基类时称这个继承关系为单继承
2.多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前面,后面继承的基类在后面,派生类成员在放到最后面。
3.菱形继承:菱形继承是多继承的一种特殊情况。菱形继承的问题,从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题,在Assistant的对象中Person成员会有两份。支持多继承就一定会有菱形继承,像Java就直接不支持多继承,规避掉了这里的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

 

菱形继承会导致数据的冗余,即一个派生类Assistant中包含两份Person的数据,虽然我们不推荐去使用菱形继承,但总有无法避免的情况使用菱形继承更好,比如IO库中的菱形继承:

(图片来源于cplusplus.com) 

这时候我们就需要使用虚继承来确保Assistant中只含一份Person的数据

4.2虚继承

 C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有
菱形虚拟继承,底层实现就很复杂,性能也会有一些损失,所以最好不要设计出菱形继承。多继承可以认为是C++的缺陷之一,后面很多的语言也就不再支持多继承比如java,虚继承的格式如下:

// 使用虚继承Person类
class Student : virtual public Person
{
protected:
    int _num; //学号
};

这时候我们去对4.1中第三幅图中的teacher与student都使用虚继承,就可以使Assistant中只有一份Person的数据,但虚继承的底层特别复杂,会带来一定的性能损耗,所以一般情况下我们尽量不要写出菱形继承这样的代码 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Python 从入门到实战43(Pandas数据结构)

我们的目标是&#xff1a;通过这一套资料学习下来&#xff0c;可以熟练掌握python基础&#xff0c;然后结合经典实例、实践相结合&#xff0c;使我们完全掌握python&#xff0c;并做到独立完成项目开发的能力。 上篇文章我们学习了NumPy数组操作的相关基础知识。今天学习一下pa…

工程项目智能化管理平台,SpringBoot框架智慧工地源码,实现工程建设施工可视化、智能化的全过程闭环管理。

智慧工地管理系统的建设以“1个可扩展性平台2个应用端3方数据融合N个智能设备”为原则。以“智、保、安、全”为导向&#xff0c;与工程建设管理信息系统、综合安防平台深度集成&#xff0c;构建统一的标准化工地平台&#xff0c;实现现场人员、车辆、项目、安全、进度等方面的…

使用React构建现代Web应用

&#x1f496; 博客主页&#xff1a;瑕疵的CSDN主页 &#x1f4bb; Gitee主页&#xff1a;瑕疵的gitee主页 &#x1f680; 文章专栏&#xff1a;《热点资讯》 使用React构建现代Web应用 1 引言 2 React简介 3 安装React 4 创建React项目 5 设计应用结构 6 创建组件 7 使用组件…

哈希——哈希表处理哈希冲突的方法

处理哈希冲突 实践中哈希表⼀般还是选择除法散列法作为哈希函数。 当然哈希表无论选择什么哈希函数也避免不了冲突&#xff08;主要作用就是减少冲突&#xff09;&#xff0c;那么插入数据时&#xff0c;如何解决冲突呢&#xff1f;主要有两种两种方法&#xff0c;开放定址法和…

海外云手机是什么?对外贸电商有什么帮助?

在外贸电商领域&#xff0c;流量引流已成为卖家们关注的核心问题。越来越多的卖家开始利用海外云手机&#xff0c;通过TikTok等社交平台吸引流量&#xff0c;以推动商品在海外市场的销售。那么&#xff0c;海外云手机到底是什么&#xff1f;它又能为外贸电商卖家提供哪些支持呢…

Hadoop-001-本地虚拟机环境搭建

一、安装VMware 官方下载VMware&#xff1a; https://vmware.mdsoft.top/?bd_vid5754305114651491003 二、下载镜像文件 阿里云镜像仓库&#xff1a; https://mirrors.aliyun.com/centos/ 本文档使用 CentOS-7-x86_64-DVD-1810-7.6.iso 搭建虚拟机 三、搭建虚拟机 1、编辑…

Oracle视频基础1.1.2练习

1.1.2 需求&#xff1a; 查询oracle组件和粒度大小&#xff0c; select component,granule_size from v$sga_dynamic_components;Oracle SGA 中组件和粒度大小查询详解 在 Oracle 数据库的内存结构中&#xff0c;SGA&#xff08;System Global Area&#xff0c;系统全局区&am…

动态上下文信念(DCB)

DCB&#xff08;动态上下文信念&#xff09;是一个用于累积通过注视获得信息的状态表示组件。它由三个部分组成&#xff1a; Fovea&#xff08;中央凹&#xff09;&#xff1a;接收来自注视位置周围区域的高分辨率视觉输入。Contextual beliefs&#xff08;上下文信念&#xf…

双月生日会:温暖相聚,共庆美好时刻

亲爱的华清远见西安中心的家人们&#xff1a; &#x1f389;&#x1f382; 在这金风送爽的秋日里&#xff0c;我们迎来了9、10月的生日会。在这个特别的日子里&#xff0c;我们聚集一堂&#xff0c;共同庆祝那些在这两个月份里出生的小伙伴们的生日。&#x1f382; 活动现场布…

Junit + Mockito保姆级集成测试实践

一、做好单测&#xff0c;慢即是快 对于单元测试的看法&#xff0c;业界同仁理解多有不同&#xff0c;尤其是在业务变化快速的互联网行业&#xff0c;通常的问题主要有&#xff0c;必须要做吗&#xff1f;做到多少合适&#xff1f;现在没做不也挺好的吗&#xff1f;甚至一些大…

【经典论文阅读11】ESMM模型——基于贝叶斯公式的CVR预估

传统的CVR模型&#xff08;也就是直接对conversion rate建模的模型&#xff09;在实际应用中面临两个问题&#xff08;样本选择偏差与数据稀疏性问题&#xff09;。为了解决这两个问题&#xff0c;本文提出ESMM模型。该模型巧妙地利用用户行为序列去建模这个问题&#xff0c;从…

使用Git进行版本控制的最佳实践

文章目录 Git简介基本概念仓库&#xff08;Repository&#xff09;提交&#xff08;Commit&#xff09;分支&#xff08;Branching&#xff09; 常用命令初始化仓库添加文件提交修改查看状态克隆仓库分支操作合并分支推送更改 最佳实践使用有意义的提交信息定期推送至远程仓库使…

Vision-Language Models for Vision Tasks: A Survey阅读笔记

虽然LLM的文章还没都看完&#xff0c;但是终究是开始看起来了VLM&#xff0c;首当其冲&#xff0c;当然是做一片文献综述啦。这篇文章比较早了&#xff0c;2024年2月份出的last version。 文章链接&#xff1a;https://arxiv.org/abs/2304.00685 GitHub链接&#xff1a;GitHu…

Oracle OCP认证考试考点详解082系列07

题记&#xff1a; 本系列主要讲解Oracle OCP认证考试考点&#xff08;题目&#xff09;&#xff0c;适用于19C/21C,跟着学OCP考试必过。 31. 第31题&#xff1a; 题目 解析及答案&#xff1a; 关于 “SET VERIFY ON” 命令&#xff0c;以下哪两个陈述是正确的&#xff1f; A…

网络搜索引擎Shodan(7)完结

声明&#xff1a;学习视频来自b站up主 泷羽sec&#xff0c;如涉及侵权马上删除文章 声明&#xff1a;本文主要用作技术分享&#xff0c;所有内容仅供参考。任何使用或依赖于本文信息所造成的法律后果均与本人无关。请读者自行判断风险&#xff0c;并遵循相关法律法规。 感谢泷…

【C++ 算法进阶】算法提升八

复杂计算 &#xff08;括号问题相关递归套路 重要&#xff09; 题目 给定一个字符串str str表示一个公式 公式里面可能有整数 - * / 符号以及左右括号 返回最终计算的结果 题目分析 本题的难点主要在于可能会有很多的括号 而我们直接模拟现实中的算法的话code会难写 要考虑…

​IOT NTN 与 NR NTN​

NTN&#xff08;Non-Terrestrial Network)&#xff09;&#xff0c;即非地面网络通信&#xff0c;通过不同轨道高度的卫星对地面上的终端提供网络连接的服务。利用卫星通信网络与地面蜂窝网络的融合&#xff0c;可以在不受地形地貌的限制和影响下&#xff0c;连通空、天、地、海…

44-RK3588s调试 camera-engine-rkaiq(rkaiq_3A_server)

在RK3588s平台上调试imx415 camera sensor 过程中&#xff0c;已经识别到了camera sensor ID&#xff0c;并且可以拿到raw图和isp处理后的图像&#xff0c;但是isp处理后的图像偏绿&#xff0c;来看查看后台服务发现rkaiq_3A_server没有运行&#xff0c;然后单独运行rkaiq_3A_s…

【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper+代码——交叉注意力(Cross-Attention)

【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper代码——交叉注意力&#xff08;Cross-Attention&#xff09; 【深度学习中的注意力机制10】11种主流注意力机制112个创新研究paper代码——交叉注意力&#xff08;Cross-Attention&#xff09; 文章目录 【…

springboot响应文件流文件给浏览器+前端下载

springboot响应文件流文件给浏览器前端下载 1.controller: Api(tags {"【样本提取系统】-api"}) RestController("YbtqYstbtqController") RequiredArgsConstructor RequestMapping("/ybtq-ystbtq") Slf4j public class YbtqYstbtqController …