C++_深究继承

文章目录

  • 1. 继承的概念和定义
    • 1.1 继承的概念
    • 1.2 继承定义
      • 1.2.1定义格式
      • 1.2.2 继承关系和访问限定符
  • 2. 基类和派生类对象赋值转换
  • 3.继承中的作用域
  • 4. 派生类的默认成员函数
  • 5. 继承和友元
  • 6. 继承与静态成员
  • 7. 菱形继承即菱形虚拟继承
      • 菱形虚拟继承
  • 8. 继承的总结与反思

1. 继承的概念和定义

1.1 继承的概念

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

其用法如下:

#include <iostream>
using namespace std;class person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}protected:string _name = "";int _age = 0;
};class student : public person
{
protected:int _id;
};class teacher : public person
{int _jobid;
};//其中,student和teacher就属于派生类(子类),而person就是这两个类的基类
//我们可以通过监视窗口查看student和teacher对象就可以观察到变量的复用,调用print可以喊道成员函数的复用。
int main()
{student s;teacher t;s.Print();t.Print();return 0;
}

在这里插入图片描述

1.2 继承定义

1.2.1定义格式

从下面就可以看到person是父类,也称为基类,而student是子类,也称为派生类。

在这里插入图片描述

1.2.2 继承关系和访问限定符

在c++中,有三种继承方式,分别是:public继承, protected继承,private继承

而访问限定符也有三种,public访问,protected访问,private访问

这两个一组合就导致了继承的机制极为复杂!

类成员/继承方式public继承protected继承private 继承
基类的public成员派生类的public成员派生类的protected成员派生类的private成员
基类的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类的private成员在派生类中不可见在派生类中不可见在派生类中不可见

总结:

  1. 基类private成员在派生类外不管是什么方式继承都是不可见的,这里的不可见是指基类成员虽然还是被继承到了派生类中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它
  2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在 派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
  3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 == min(成员在基类的访问限定符,继承方式),public > protected > private
  4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过 最好显示的写出继承方式。
  5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强

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

  • 派生类对象可以赋值给基类的对象/指针/引用。形象的说法可以说是切片或切割,意思是把派生类中父类那部分切过来赋值过去。

需要注意的是在赋值的时候并没有调用拷贝构造,而是直接将里面的内容拷贝过去。

  • 基类对象不能赋值给派生类对象

这也很容易理解,如果能赋值过去,那么子类对象多的成员不会知道应该赋值成什么

  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但必须是基类的指针指向派生类对象时才安全。
  • 在这里插入图片描述

3.继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域
  2. 子类和父类中有同名成员,派生类将屏蔽基类对同名成员的直接访问,这种情况叫做**隐藏(也叫做重定义)。**如果在定义有重名成员变量或函数的时候想要访问基类的成员,可以使用访问限定符来显示访问。
class person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}protected:string _name = "";int _age = 0;
};class student : public person
{
protected:string _name;int _id = 0;
public:void print(){cout << _id << endl;}
};
int main()
{//在这种情况下想要访问基类的print()函数,应该使用访问限定符student s;s.person::print();//所以在设计继承时最好不要定义同名成员,否则会出现二义性问题
}
  1. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  2. 注意在实际使用中继承体系里面最好不要定义同名成员

4. 派生类的默认成员函数

在学习c++类和对象的时候,我们知道对于类来说有六个我们不写也会自动生成的默认成员函数,那么在派生类中,这些默认成员函数是如何生成的呢?

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。(也就是说基类的成员变量只能通过基类构造函数构造,如果没有显示调用,则编译器会自动调用默认构造函数)。如果基类没有默认构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
  2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的初始化
  3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员(也就是不需要显示调用基类的析构函数)。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  5. 派生类对象初始化先调用基类构造再调派生类构造。
  6. 编译器会对析构函数名进行特殊处理,处理程destrutor(),便于后面的重写做铺垫(注意,重写和重定义是两个不同的概念!!),因此父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

5. 继承和友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

通俗一点理解,父亲的朋友不是你的朋友。

class student;
class person
{friend void display(const person& p, const student& s);
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}protected:string _name = "";int _age = 0;
};class student : public person
{
protected:int _id = 0;
};class teacher : public person
{int _jobid = 0;
};void display(const person& p, const student& s)
{cout << p._name << endl;cout << s._id << endl;
}

在这里插入图片描述

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个成员。无论派生出多少个子类,都只有一个static成员实例。

#include <iostream>
using namespace std;
class person
{
public:static int _num;person() { ++_num; }void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}
protected:string _name = "";int _age = 0;
};int person::_num = 0;class student : public person
{
protected:int _id = 0;
};class teacher : public person
{int _jobid = 0;
};int main()
{student s;teacher t;person p;cout << person::_num << endl;return 0;
}

在这里插入图片描述

7. 菱形继承即菱形虚拟继承

**单继承:**一个子类只有一个直接父类时称这个继承关系为单继承。

在这里插入图片描述

多继承:一个子类友两个或以上直接父类时称这个继承关系为多继承(只要保证父类中都没有同名成员就不会二义性问题)。
在这里插入图片描述

**菱形继承:**菱形继承时多继承的一种特殊情况

在这里插入图片描述

这种继承方式存在的问题是:可能会存在数据冗余和二义性的问题!看下图:
在这里插入图片描述

class person
{
public:string _name;int _age;
};class student: public person
{
protected:int _id;
};class teacher : public person
{
protected:int _num;
};class assistant : public student, public teacher
{
protected:string _course;
};int main()
{assistant s;cout << s._name << endl;}

在这里插入图片描述
然后我们可以通过观看对象模型就可以看到问题出现在哪
在这里插入图片描述

如果想要解决二义性问题,可以使用指定访问,如s.student::_name,s.teacher::_name,但是,这样的方法仍然不能解决数据冗余的问题,想要解决这个问题,就需要使用虚拟继承。

菱形虚拟继承

虚拟继承可以解决虚拟继承的二义性和数据冗余的问题。如上面的继承关系,在student和teacher继承person时使用虚拟继承(在继承体系的腰部),即可解决问题。

class person
{
public:int _age = 0;
};class student: virtual public person
{
public:int _id = 0;
};class teacher : virtual public person
{
public:int _num = 0;
};class assistant : public student, public teacher
{
public:int _t = 0;
};int main()
{assistant s;cout << s._age << endl;return 0;}

通过使用虚继承,就可以使得assistant对象中只存在一个person成员,但是虚拟继承是如何做到的呢?我们可以通过借助内存窗口观察对象模型得知。
在这里插入图片描述
通过分析可以看到,assistant对象将person对象的数据放到了对象组成的最下面单独出来,而本来student成员和teacher成员里面的person成员则变成了两个指针,而这两个指针指向的是什么呢?同样我们通过监视窗口观察一下。
在这里插入图片描述
通过观察我们就可以发现,这两个指针指向的地方存的下一个位置储着一个数据指向从该地方到虚继承成员的数据存储地址,存的是偏移量。通过测试,我们就知道了虚继承是如何解决数据冗余的问题的了。

虚拟继承中student和teacher对象模型中的这两个指针叫做虚基表指针,这两个表叫做虚基表。虚基表中存的是偏移量,通过偏移量找到下面的person。
下面是对菱形虚拟继承的原理解释:
在这里插入图片描述

8. 继承的总结与反思

  1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱 形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
  2. 多继承可以认为是C++的缺陷之一,很多后来的OO(object oritanted)语言都没有多继承,如Java
  3. 继承和组合
  • public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
  • 组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
  • 优先使用对象组合,而不是类继承
  • 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用称为白箱复用。术语“白箱”是相对可视性而言:在继承关系中,基类的大部分细节对派生类可见。继承一定程度的破坏了基类的封装,基类的改变对派生类的影响有可能很大,派生类和基类之间的依赖关系较强,耦合度很高
  • 对象组合是继承之外的另一种复用选择。新的更复杂的功能可以通过组装或者组合对象来获得。对象组合要求被组合的对象具有良好的接口。这种复用风格被称为黑箱复用,因为对象的内部细节是不可见的。对象只以“黑箱”的方式呈现。组合类之间的依赖关系较弱,耦合度第。优先使用组合有助于保持每个类的独立性。
  • 实际中应尽量多的取用组合。组合的耦合度低,代码维护性好。不过继承也有其用处,并且多态也只能通过继承实现。简单来说,只有组合无法完成的任务我们才使用继承。

以上就是关于c++继承的主要内容了,如果大家对本内容还有什么疑惑或者博主哪里说法有误的话,欢迎大家在评论区指出!

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

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

相关文章

Linux(进程控制)

进程控制 进程创建fork函数初识fork函数返回值写时拷贝fork常规用法fork调用失败的原因 进程终止进程退出码进程常见退出方法 进程等待进程等待必要性获取子进程status进程等待的方法 阻塞等待与非阻塞等待阻塞等待非阻塞等待 进程替换替换原理替换函数函数解释命名理解 做一个…

Python opennsfw/opennsfw2 图片/视频 鉴黄 笔记

nsfw&#xff08; Not Suitable for Work&#xff09;直接翻译就是 工作的时候不适合看&#xff0c;真文雅 nsfw效果&#xff0c;注意底部的分数 大体流程&#xff0c;输入图片/视频&#xff0c;输出0-1之间的数字&#xff0c;一般情况下&#xff0c;Scores < 0.2 认为是非…

20230811导出Redmi Note12Pro 5G手机的录音机APP的录音

20230811导出Redmi Note12Pro 5G手机的录音机APP的录音 2023/8/11 10:54 redmi note12 pro 录音文件 位置 貌似必须导出录音&#xff0c;录音的源文件不知道存储到哪里了&#xff01; 参考资料&#xff1a; https://jingyan.baidu.com/article/b87fe19e9aa79b1319356842.html 红…

【数学建模】--灰色关联分析

系统分析: 一般的抽象系统&#xff0c;如社会系统&#xff0c;经济系统&#xff0c;农业系统&#xff0c;生态系统&#xff0c;教育系统等都包含有许多种因素&#xff0c;多种因素共同作用的结果决定了该系统的发展态势。人们常常希望知道在众多的因素中&#xff0c;哪些是主要…

history记录日期时间和日志记录操作

history命令能查看到操作日期和时间的配置方法&#xff1a; 1&#xff09;在/etc/profile文件中添加一行&#xff1a; export HISTTIMEFORMAT"%F %T whoami " 2&#xff09;保存后&#xff0c;执行加载命令&#xff1a; source /etc/profile 3&#xff09;然后检…

分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测

分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测 目录 分类预测 | MATLAB实现BO-BiGRU贝叶斯优化双向门控循环单元多输入分类预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现BO-BiGRU贝叶斯优化双向门控循环单元多特征分…

Springboot 实践(4)swagger-ui 测试controller

前文项目操作&#xff0c;完成了项目的创建、数据源的配置以及数据库DAO程序的生成与配置。此文讲解利用swagger-ui界面&#xff0c;测试生成的数据库DAO程序。目前&#xff0c;项目swagger-ui界面如下&#xff1a; 以”用户管理”为例&#xff0c;简单讲述swagger-ui测试数据库…

Ghost-free High Dynamic Range Imaging withContext-aware Transformer

Abstract 高动态范围(HDR)去鬼算法旨在生成具有真实感细节的无鬼HDR图像。 受感受野局部性的限制&#xff0c;现有的基于CNN的方法在大运动和严重饱和度的情况下容易产生重影伪影和强度畸变。 本文提出了一种新的上下文感知视觉转换器&#xff08;CA-VIT&#xff09;用于高动态…

优维低代码实践:自定义模板

优维低代码技术专栏&#xff0c;是一个全新的、技术为主的专栏&#xff0c;由优维技术委员会成员执笔&#xff0c;基于优维7年低代码技术研发及运维成果&#xff0c;主要介绍低代码相关的技术原理及架构逻辑&#xff0c;目的是给广大运维人提供一个技术交流与学习的平台。 优维…

亿赛通电子文档安全管理系统任意文件上传漏洞复现

0x01 产品简介 亿赛通电子文档安全管理系统&#xff08;简称&#xff1a;CDG&#xff09;是一款电子文档安全加密软件&#xff0c;该系统利用驱动层透明加密技术&#xff0c;通过对电子文档的加密保护&#xff0c;防止内部员工泄密和外部人员非法窃取企业核心重要数据资产&…

由浅入深学习Tapable

文章目录 由浅入深学习TapableTapable是什么Tapable的Hook分类同步和异步的 使用Sync*同步类型钩子基本使用bailLoopWaterfall Async*异步类型钩子ParallelSeries 由浅入深学习Tapable webpack有两个非常重要的类&#xff1a;Compiler和Compilation。他们通过注入插件的方式&a…

线上售楼vr全景看房成为企业数字化营销工具

在房地产业中&#xff0c;VR全景拍摄为买家提供了虚拟看房的全新体验。买家可以通过相关设备&#xff0c;远程参观各个楼盘的样板间和实景&#xff0c;感受房屋的空间布局和环境氛围&#xff0c;极大地提高了购房决策的准确性。对于房地产开发商和中介机构来说&#xff0c;VR全…