【C++进阶(八)】C++继承深度剖析

news2024/11/18 12:33:29

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:C++从入门到精通⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你学习C++
  🔝🔝


在这里插入图片描述

继承

  • 1. 前言
  • 2. 继承的基本概念
  • 3. 继承关系和访问限定符
  • 4. 继承中的作用域
  • 5. 父子类的对象赋值转换
  • 6. 子类中的默认成员函数
  • 7. 继承与友元,继承与静态变量
  • 8. 菱形继承和虚拟继承
  • 9. 总结以及拓展

1. 前言

接下来的几篇博客会进入C++
继承和多态的学习,在校招笔试
和面试中这一章节考察的很多!
请同学们耐心学习!

本章重点:

本篇文章着重讲解继承的概念和定义,
父类和子类的对象赋值转换,
继承中的作用域以及子类的默认成员函数
以及继承和友元,继承和静态成员的关系
最后讲解菱形继承和虚继承概念


2. 继承的基本概念

继承,其实就是一种代码的复用手段
子类继承父类就能用父类中的变量!

举一个例子:

在师生管理系统中,有学生和老师两个
角色,学生和老师的共同信息有:姓名
性别,年龄和身高等等,然而学生又有一些
专有的信息,比如学号和所属学院
老师也有专属信息如:工号和所教学科

这样就可以将师生的共同信息提取出来:

struct Person
{
	string name;
	string sex;
	int age;
	int height;
}

在实现student类和teacher类时
只需要继承上面的person类即可!

class Student : public Person
{
protected:
	int _stuid; // 学号
};
class Teacher : public Person
{
protected:
	int _jobid; // 工号
};

在这里插入图片描述

派生类也被称为子类
基类也被称为父类

父子类成员的使用:

Student st;
st._stuid=123456;
st.name="张三";
st.age=20;

子类对象中课直接调用父类变量!


3. 继承关系和访问限定符

继承方式和访问限定符一样有三种:
在这里插入图片描述

继承的方式不同,那么子类中继承
到的父类的变量的访问权限就不同
可以用下面的表格来表示它们的关系:

在这里插入图片描述
我简单的总结以下几点:

  1. 无继承体系中,protected和private没有区别
  2. 在继承体系中,父类的protected成员在子类
    中也是protected或保护成员
  3. 父类的private成员在子类是不可见的!
    (继承下来了但不能使用)
  4. 实际中使用继承时一般都用public继承
  5. 使用关键字class时默认的继承方式是private
    使用struct时默认的继承方式是public

4. 继承中的作用域

先说以下结论:

  1. 继承体系中基类和子类有独立的作用域
  2. 子类和父类中有同名成员时,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义(在子类成员函数中,可以使用基类::基类成员显示访问)
  3. 需要注意的是如果是成员函数的隐藏
    只需要函数名相同就构成隐藏
  4. 实际中在继承体系里面最好不要定义同名的成员
class Person
{
protected :
	int _num = 111;   // 身份证号
};

class Student : public Person
{
protected:
	int _num = 999; // 学号
};

在main函数中定义student对象
后再打印_num默认为子类中的_num
若想打印父类中的_num,需要指定类域

Student st;
cout<<st._num;
cout<<st.Person::_num;

有同学可能会疑惑:函数名相同的话
不应该是构成函数重载吗?是的,在同一
作用域下,函数名相同确实构成函数重载
但是父子类是不同作用域,这里是构成隐藏!


5. 父子类的对象赋值转换

老样子,先说结论:

  • 子类对象可以赋值给基类的
    -/对象/基类的指针/基类的引用

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

在这里插入图片描述

注意这里能够赋值不是隐式类型转换!


6. 子类中的默认成员函数

还记得类的六个默认成员函数吗?
就是不显示写系统会自动生成的:

在这里插入图片描述

子类的默认成员函数有哪些特殊的行为?
下面我直接给出结论:

  1. 子类的构造函数必须显示调用父类的构造
    去初始化父类的那部分成员(拷贝构造也是)
  2. 子类的operator=中必须调用父类的
    operator=完成父类成员赋值
  3. 子类的析构函数不用显示调用父类的析构
    编译器会自动去调用
  4. 子类初始化对象时,先初始化父类的成员变量
    再初始化子类的成员变量
  5. 子类析构清理时先调用子类的析构函数
    再调用父类的析构函数(与构造反过来)

可以使用以下代码区验证此结论:

class Person
{
public :
 Person(const char* name = "peter")
 : _name(name )
 {
 cout<<"Person()" <<endl;
 }
    
 Person(const Person& p)
 : _name(p._name)
 {
 	cout<<"Person(const Person& p)" <<endl;
 }
    
 Person& operator=(const Person& p )
 {
 	cout<<"Person operator=(const Person& p)"<< endl;
 	if (this != &p)
 	_name = p ._name;
        
 	return *this ;
 }
    
 ~Person()
 {
 	cout<<"~Person()" <<endl;
 }
protected :
 	string _name ; // 姓名
};
class Student : public Person
{
public :
 Student(const char* name, int num)
 	: Person(name )
 	, _num(num )
 	{
 	cout<<"Student()" <<endl;
 	}
 
 Student(const Student& s)
 	: Person(s)
 	, _num(s ._num)
 {
 	cout<<"Student(const Student& s)" <<endl ;
 }
 
 Student& operator = (const Student& s )
 {
 	cout<<"Student& operator= (const Student& s)"<< endl;
 	if (this != &s)
 	{
 		Person::operator =(s);
		_num = s ._num;
 	}
 return *this ;
 } 
 
 ~Student()
 {
 	cout<<"~Student()" <<endl;
 }
protected :
 	int _num ; //学号
};
void Test ()
{
 	Student s1 ("jack", 18);
 	Student s2 (s1);
 	Student s3 ("rose", 17);
 	s1 = s3 ;
}

7. 继承与友元,继承与静态变量

继承与友元的关系很简单一句话:

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

继承和静态成员的关系也很简单:

基类中定义的静态成员被整个继承体系共享
整个继承体系里面只有一个这样的成员
无论派生出多少个子类
都只有一个static成员实例


8. 菱形继承和虚拟继承

在使用继承时会遇见以下情况:

类B继承了类A,类C也继承了类A
然而类D继承了类B和C

在这里插入图片描述

此时会有一个问题,类D的实例化对象中
有类B和类C,然而B类和C类都有A类
所以说D类对象中的A类成员就重复了!

class A
{
	int _a = 1;
};
class B :public A
{
	int _b = 2;
};
class C :public A
{
	int _c = 3;
};
class D :public B, A
{
	int _d = 4;
};

我们通过内存窗口观察一下:

在这里插入图片描述

D对象中有两个_a,一个在B类一个在C类
这就造成了数据冗余,于是可以使用虚拟继承
来解决这一问题:

虚拟继承:在继承前加上virtual关键字

class A
{
	int _a = 1;
};
class B :virtual public A
{
	int _b = 2;
};
class C :virtual public A
{
	int _c = 3;
};
class D :public B, A
{
	int _d = 4;
};

注意,只用腰部的类加上virtual即可!
virtual这一关键字在多态还有大用处!


9. 总结以及拓展

继承是多态的基础,而笔试面试的时候
继承和多态是考察的很多的,希望同学们
把基础打扎实.当然关于继承的内容其实不止
这些,这些只是最重要的内容,关于继承问题
我们将在下一章节:多态时再展开叙述

对于is-a和has-a的拓展阅读:

拓展阅读


🔎 下期预告:C++继多态 🔍

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

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

相关文章

全球二氧化碳排放数据1deg产品(ODIAC)数据

简介 全球二氧化碳排放数据1deg产品(ODIAC)是一个空间分辨率为1deg*1deg的全球化石燃料燃烧产生的二氧化碳空间分布产品。它率先将基于空间的夜间灯光数据与单个发电厂的排放/位置相结合来估计化石燃料二氧化碳的排放。该产品被国际研究界广泛用于各种研究应用&#xff08;例如…

微信小程序的OA会议之首页搭建

目录 一.小程序的布局 1.1. flex是什么 1.2. flex布局 1.3.总体布局 二.轮播图 2.1. 组件 2.2. 数据请求 2.3. 页面 三.首页 2.1. 视图 2.2.数据 2.3. 样式 好啦今天就到这里了&#xff0c;希望能帮到你哦&#xff01;&#xff01;&#xff01; 一.小程序的布局 …

Linux入门攻坚——4、shell编程初步、grep及正则表达式

bash的基础特性&#xff08;续&#xff09;&#xff1a; 1、提供了编程环境&#xff1a; 编程风格&#xff1a;过程式&#xff1a;以指令为中心&#xff0c;数据服务于执行&#xff1b;对象式&#xff1a;以数据为中心&#xff0c;指令服务于数据 shell编程&#xff0c;编译执…

面向JS程序员的TypeScript

定义类型 你可以在 JavaScript 中使用多种设计模式。 但是&#xff0c;某些设计模式使得自动推断类型变得困难&#xff08;例如&#xff0c;使用动态编程的模式&#xff09;。 为了涵盖这些情况&#xff0c;TypeScript 支持 JavaScript 语言的扩展&#xff0c;它为你提供了告诉…

YOLOv5改进实战 | 更换主干网络Backbone之轻量化网络EMO,结合 CNN 和 Transformer 的现代倒残差移动模块设计

前言 传统的YOLOv5系列中,Backbone采用的是较为复杂的C3网络结构,这使得模型计算量大幅度的增加,检测速度较慢,应用受限,在某些真实的应用场景如移动或者嵌入式设备,如此大而复杂的模型时难以被应用的。为了解决这个问题,本章节通过采用EMO轻量化主干网络作为Backbone的…

Python+unittest+requests接口自动化测试框架搭建 完整的框架搭建过程

首先配置好开发环境&#xff0c;下载安装Python并下载安装pycharm&#xff0c;在pycharm中创建项目功能目录。如果不会的可以百度Google一下&#xff0c;该内容网上的讲解还是比较多比较全的&#xff01; 大家可以先简单了解下该项目的目录结构介绍&#xff0c;后面会针对每个文…

黑豹程序员-架构师学习路线图-百科:MVC的演变终点SpringMVC

MVC发展史 在我们开发小型项目时&#xff0c;我们代码是混杂在一起的&#xff0c;术语称为紧耦合。 如最终写ASP、PHP。里面既包括服务器端代码&#xff0c;数据库操作的代码&#xff0c;又包括前端页面代码、HTML展现的代码、CSS美化的代码、JS交互的代码。可以看到早期编程就…

mysql反斜杠多次转义简述

概述 通常在业务中会用正则/like去匹配一些数据.如果数据中有反斜杠&#xff0c;或者是用的正则语句。 那么在写sql的时候就需要转义下反斜杠&#xff0c;转义的时候有几个隐藏问题需要注意。 使用like做where条件 如下使用like要查询这两条数据的时候&#xff0c;需要用8个…

Android音视频开发之基础知识

一、视频文件 1、视频格式 常见格式&#xff1a;mp4、mkv、flv 封装的数据&#xff1a;音频码流、视频码流 常用工具&#xff1a; [FFmpeg下载]:https://ffmpeg.org/download.html 下载、安装并配置环境变量 ffmpeg.exe 视频编解码 ffplay.exe 播放器库 ffprobe.exe 音视频分…

【三:Spring整合Mybatis】

目录 Spring整合Mybatis&#xff08;基于xml文件&#xff09;1 、项目的大体框架前期准备1.2 、使用Navicat在数据库中创建一张表account1.3、 在pom.xml文件中加入maven&#xff08;依赖的配置&#xff09;1.4、配置相应的数据库连接&#xff0c;以及扫描相应的dao层 编写实体…

Unity中Shader实现UI流光效果

文章目录 前言一、实现思路1&#xff1a;1、采集两张贴图&#xff0c;一张是主纹理&#xff0c;一张是扫光纹理2、在 v2f 定义一个二维变量 “uv2” 来存放 uv 偏移后的值3、在顶点着色器中&#xff0c;仿照之前的 uv 流动效果,与 _Time相乘后存放于 uv2 中4、最后&#xff0c;…

微信小程序中封装请求,使用Async await方法,将异步请求变为同步请求方法

介绍 微信小程序中&#xff0c;很多 API都是异步的&#xff0c;无法同步处理。可以使用高级封装&#xff0c;通过async await方法来同步处理。 方法 在小程序右上角的 详情 里选择 本地设置 , 勾选 ES6转ES5&#xff0c;如下所示&#xff1a; 由于 Async Await 是 ES7语法&a…

Boost.Beast和C++编写程序

以下是一个使用Boost.Beast和C编写的爬虫程序&#xff0c;用于爬取。此程序使用了proxy的代码。 #include <iostream> #include <string> #include <boost/asio.hpp> #include <boost/beast.hpp> ​ std::string get_audio_url(const std::string&…

第十七章 Java连接数据库

1.打卡“命令提示符”&#xff0c;用管理员身份运行 2.登录MySQL 3.创建库和表 4.使用Java命令查询数据库操作 5.右击——点击“Build Path”——选择第四个——找到包的位置——导入成功 一、创建java项目 二、连接数据库 1.注册驱动 2.获取链接 3.获取statment对象 4.…

docker命令实例(举例子学习)

docker命令实例 实例1 docker build -t linksoul/chinese-llama2-chat .实例2 docker run --gpus all --ipchost --ulimit memlock-1 --ulimit stack67108864 --rm -it -v pwd/LinkSoul:/app/LinkSoul -p 7860:7860 linksoul/chinese-llama2-chat实例3 docker run -it -p 78…

QT串口接收数据并进行波形显示(含源码)

**使用QT在串口调试助手基础上实现波形显示&#xff08;含源码&#xff09; 评论比较多留言需要源码的&#xff0c;逐个发邮箱比较麻烦也不能及时回复&#xff0c;现将源码上传至链接&#xff08;无需积分下载&#xff09;https://download.csdn.net/download/m0_51294753/877…

Jupyter Notebook 设置黑色背景主题

Jupyter Notebook 设置黑色背景主题 # 包安装 pip install jupyterthemes -i https://mirrors.aliyun.com/pypi/simple pip install --upgrade jupyterthemes # 查看可用主题 jt -l # monokai暗背景&#xff0c;-f(字体) -fs(字体大小) -cellw(占屏比或宽度) -ofs(输出段的字…

自己DIY一个模拟多按钮电路

普通按键电路原理&#xff1a; 一般使用的按键原理图如下图所示&#xff0c;由按键、上拉电阻和消抖滤波电容组成。按键断开时KeyIin1处电压被上拉到5V&#xff0c;当按键闭合时把KeyIin1电压拉到0V&#xff0c;与按键并联的电容起到滤除按键按下与弹起时的高频信号。 如果在电…

让uniGUI支持https

今天在专家的帮助下&#xff0c;成功的让uniGUI支持https了。 首先&#xff0c;去申请个**的证书。我同事去阿里申请的&#xff0c;申请回是一个zip文件&#xff0c;里面有两个文件&#xff0c;一个扩展是per&#xff0c;一个key 然后&#xff0c;把这两个证书文件放到uniGUI…

【LeetCode】48. 旋转图像

1 问题 给定一个 n n 的二维矩阵 matrix 表示一个图像。请你将图像顺时针旋转 90 度。 你必须在 原地 旋转图像&#xff0c;这意味着你需要直接修改输入的二维矩阵。请不要 使用另一个矩阵来旋转图像。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,2,3],[4,5,6],[7,8…