当前位置: 首页 > news >正文

C++笔记---继承(上)

1. 继承的简单介绍

1.1 继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进行扩展,增加方法(成员函数)和属性(成员变量),这样产生新的类,称派生类。

继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的复用,继承是类设计层次的复用。

简单来说,被继承的类叫做父类(或基类),继承自父类的类叫做子类(或派生类)

子类拥有父类的所有成员,在此基础之上可以对父类进行拓展。

1.2 子类的定义方式

class 子类名 : 访问限定符 父类名
{// 拓展内容
}

通常来说,父类和子类具有类别上的包含关系

例如,老师和同学不仅具有人的基本特点,还在人的基础之上有了自己的拓展,而老师和同学都属于人。

我们在用C++进行描述的时候,就可以将老师和同学设计成人的子类:

人类(父类):

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.3 继承方式

访问限定符限定的是继承的方式,不同访问限定符下的继承方式如下:

类成员 / 继承方式publicprotectedprivate
父类的public成员子类的public成员子类的protected成员子类的private成员
父类的protected成员子类的protected成员子类的protected成员子类的private成员
父类的private成员子类无法直接显式访问子类无法直接显式访问子类无法直接显式访问

子类成员的访问权限:public > protected > private > 父类中被修饰为private

其中,protected访问限定符是伴随着继承的出现而出现的。被它修饰的成员,意味着无法在类外部进行访问,而可以在类内部或其子类中被访问

无论子类以何种方式继承,在父类中被修饰为private的成员子类都不可直接显式访问。

继承方式用于限定继承下来的成员访问权限不能高于继承方式。

当不指定继承方式时,class子类默认为private方式继承,struct子类默认为public方式继承。

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

可以采用protected/private继承的一个例子:

#include<vector>
#define CONTAINER std::vectortemplate<class T>
class stack : private CONTAINER<T>
{
public:void push(const T& x){CONTAINER<T>::push_back(x);}void pop(){CONTAINER<T>::pop_back();}const T& top(){return CONTAINER<T>::back();}bool empty(){return CONTAINER<T>::empty();}
};int main()
{stack<int> st;st.push(1);st.push(2);st.push(3);while (!st.empty()){cout << st.top() << " ";st.pop();} return 0;
}

这里采用了继承vector的方式来实现stack,但我希望用户只用stack的接口而不直接访问vector的成员函数,此时就可以采取protected/private的继承方式。

1.4 继承类模板

相信细心的小伙伴已经发现了,在上面stack的例子中,我们每次调用vector的接口时都对其类域进行了指定,否则会发生编译报错:

error C3861: “push_back”: 找不到标识符

这是因为,模板是按需实例化的,当类模板中的函数没有被调用时,其就不会实例化

我们在实例化stack<T>对象时,vector<T>对象也跟着实例化。但是vector<T>中,只有成员变量和构造函数被实例化了

我们在调用push_back()函数时,由于this指针为stack<T>类型,所以编译器并不会将vector模板中的push_back()函数实例化,而是直接去寻找其定义或声明

所以,继承模板类时,调用父类函数要注意指定类域。

2. 父类和子类的转换

1. public继承的子类对象可以赋值给父类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把子类中父类那部分切出来,父类指针或引用指向的是子类中切出来的父类那部分。

2. 父类对象不能赋值给子类对象。

3. 父类的指针或者引用可以通过强制类型转换赋值给子类的指针或者引用。但是必须是父类的指针是指向子类对象时才是安全的。(这里父类如果是多态类型,可以使用RTTI(Run-Time TypeInformation)的dynamic_cast 来进行识别后进行安全转换)

class Person
{
protected:string _name; // 姓名string _sex; // 性别int _age; // 年龄
};class Student : public Person
{
public:int _No; // 学号
};int main()
{Student sobj;// 1.派⽣类对象可以赋值给基类的指针/引⽤Person* pp = &sobj;Person& rp = sobj;// ⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的Person pobj = sobj;//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错sobj = pobj;return 0;
}

 3. 继承中的作用域及"隐藏"规则

隐藏规则:

1. 在继承体系中父类和子类都有独立的作用域。
2. 子类和基类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏。(在子类成员函数中,可以使用"父类::父类成员"显式访问)

// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是⾮常容易混淆
class Person
{
protected:string _name = "⼩李⼦"; // 姓名int _num = 111; // ⾝份证号
};class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " ⾝份证号:" << Person::_num << endl;cout << " 学号:" << _num << endl;}
protected:int _num = 999; // 学号
};int main()
{Student s1;s1.Print();return 0;
};

 这里,程序运行的结果为

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同(分别在父类和子类中定义的函数只会触发隐藏而不会触发函数重载)就构成隐藏。
4. 注意在实际中在继承体系里面最好不要定义同名的成员

class A
{
public:void fun(){cout << "func()" << endl;}
};class B : public A
{
public:void fun(int i){cout << "func(int i)" << i << endl;}
};

此时,B中的fun函数会隐藏掉A中的fun函数,而不会构成重载。

4. 子类的默认成员函数

子类的默认成员函数主要是用于处理父类没有的成员成员变量,父类自身的成员交由其自己的默认成员函数去处理。

4.1 构造函数

子类在构造函数的初始化列表中可以显式调用父类的构造函数对父类成员变量进行初始化,若未显式调用则会调用父类的默认构造函数。

如果没有显式调用父类的构造函数,且父类没有默认构造函数,那么此时就会发生报错。

显式调用父类构造函数的方式是:

父类名(参数列表)
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;
}

4.2 赋值重载

必须要在子类的operator=中显式调用父类的operator=才能按照预期正常的对父类成员进行拷贝,否则只会完成浅拷贝。

要注意,子类的operator=会隐藏父类的operator=,调用父类的operator=需要指定类域。

Student& operator = (const Student& s)
{cout << "Student& operator= (const Student& s)" << endl;if (this != &s){// 构成隐藏,所以需要显⽰调⽤Person::operator =(s);_num = s._num;} return* this;
}

4.3 析构函数

父类的析构函数会在子类的析构函数被调用之后自动调用,无需显式调用。

相关文章:

C++笔记---继承(上)

1. 继承的简单介绍 1.1 继承的概念 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段&#xff0c;它允许我们在保持原有类特性的基础上进行扩展&#xff0c;增加方法(成员函数)和属性(成员变量)&#xff0c;这样产生新的类&#xff0c;称派生类。 继承呈…...

气膜体育馆:为学校打造智能化运动空间—轻空间

随着教育体制的逐步升级&#xff0c;学校在提升学生综合素质方面的需求日益增长&#xff0c;特别是在体育场地方面。气膜体育馆作为一种新型的运动空间形式&#xff0c;正在迅速成为学校体育设施的优选方案。凭借其快速搭建、节能环保等优势&#xff0c;气膜馆在全国各地的校园…...

JVM 调优篇5 jvm性能监控

一 jvm性能监控 1.1 概述 性能诊断是软件工程师在日常工作中需要经常面对和解决的问题&#xff0c;在用户体验至上的今天&#xff0c;解决好应用的性能问题能带来非常大的收益。 体会1&#xff1a;使用数据说明问题&#xff0c;使用知识分析问题&#xff0c;使用工具处理问…...

一. Unity实现虚拟摇杆及屏幕自适应功能

手游里面很多类型的游戏都需要用到遥感功能&#xff0c;例如王者荣耀&#xff0c;和平精英等&#xff0c;之前的摇杆功能都是用类似于Easy Touch的插件进行开发的&#xff0c;今天不借助任何插件来实现虚拟摇杆的功能。 一般虚拟摇杆的组成都是由轮盘和遥感的点组成&#xff0c…...

【达梦数据库】mysql 和达梦 tinyint 与 bit 返回值类型差异

测试环境 mysql5.7.44 达梦2024Q2季度版 前言 在mysql 中存在 tinyint&#xff08;1&#xff09;的用法来实现存储0 1 作为boolean的标识列&#xff1b;但是在达梦并不允许使用 tinyint&#xff08;1&#xff09;来定义列&#xff0c;只能使用 tinyint 即 取值范围为&#xff…...

VUE工程中axios基本使用

安装axios npm install axios -s在main.js中引入 import http from axios Vue.prototype.$http = http将其绑定在VUE的prototype属性中 vue工程目录下,新建config文件夹,在config文件夹下新建index.js export default {...

跨服务器执行PowerShell脚本

本机和远程机都要执行 Enable-PSRemoting -Force 远程端关闭公用网络 Get-NetConnectionProfile Set-NetConnectionProfile -Name "未识别的网络" -NetworkCategory Private 本机和远程机都要执行 winrm quickconfig 将远程机ip加入信任列表 cd WSMan::localhost\…...

linux_L2_linux删除文件

linux 删除文件 在Linux下删除文件有多种实现方法&#xff0c;以下是其中几种常见的方法&#xff1a; 方法一&#xff1a;使用rm命令删除单个文件 rm 文件路径例如&#xff0c;删除当前目录下的文件file.txt&#xff1a; rm file.txtQuestion :当你在Linux系统中使用rm命令删…...

系统架构设计师 - 项目管理

项目管理 项目管理&#xff08;1-3分&#xff0c;案例分析 25分&#xff09;立项管理 ★盈亏平衡分析 范围管理 ★★时间管理 ★★★★概述前导图法 PDM(单代号网络图)箭线图法 ADM(双代号网络图) 了解关键路径法总时差自由时差 甘特图 成本管理 ★挣值管理概述指数计算 软件质…...

Spring Boot基础

项目创建 项目启动 请求响应 RestController 1.返回值处理 RestController&#xff1a;这个注解结合了Controller和ResponseBody的功能。它默认将所有处理请求的方法的返回值直接作为响应体内容返回&#xff0c;主要用于构建RESTful API。返回的数据格式通常是JSON或XML&…...

C语言 | Leetcode C语言题解之第402题移掉K位数字

题目&#xff1a; 题解&#xff1a; char* removeKdigits(char* num, int k) {int n strlen(num), top 0;char* stk malloc(sizeof(char) * (n 1));for (int i 0; i < n; i) {while (top > 0 && stk[top] > num[i] && k) {top--, k--;}stk[top]…...

使用Visual Studio Code配置C/C++开发环境的全面指南

目录 引言 一、准备工作 1. 安装Visual Studio Code 2. 安装C/C编译器 3. 配置环境变量&#xff08;仅Windows用户&#xff09; 二、在VS Code中安装C/C扩展 三、创建您的第一个C/C项目 1. 创建项目文件夹 2. 打开项目文件夹 3. 创建源文件 四、配置任务&#xff08;…...

算法练习题26——多项式输出(模拟)

输入格式 输入共有 2 行 第一行 1 个整数&#xff0c;n&#xff0c;表示一元多项式的次数。 第二行有 n1 个整数&#xff0c;其中第 i 个整数表示第 n−i1 次项的系数&#xff0c;每两个整数之间用空格隔开。 输出格式 输出共 1 行&#xff0c;按题目所述格式输出多项式。…...

卷积神经网络经典模型架构简介

【图书推荐】《PyTorch深度学习与企业级项目实战》-CSDN博客 《PyTorch深度学习与企业级项目实战&#xff08;人工智能技术丛书&#xff09;》(宋立桓&#xff0c;宋立林)【摘要 书评 试读】- 京东图书 (jd.com) ImageNet是一个包含超过1 500万幅手工标记的高分辨率图像的数据…...

【Kubernetes】常见面试题汇总(十三)

目录 39.简述 Kubernetes Scheduler 使用哪两种算法将 Pod 绑定到 worker 节点&#xff1f; 40.简述 Kubernetes kubelet 的作用&#xff1f; 41.简述 Kubernetes kubelet 监控 Worker 节点资源是使用什么组件来实现的&#xff1f; 39.简述 Kubernetes Scheduler 使用哪两种算…...

嵌入式QT开发:构建高效智能的嵌入式系统

摘要&#xff1a; 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发&#xff0c;详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程&#xff0c;包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面…...

Linux抢占调度

目录 抢占流程 抢占时机 用户态抢占时机 1、 从系统调用返回用户空间 2、 从中断返回用户空间 内核态抢占时机 1、中断处理程序返回内核空间 可以看到最终是到了 preempt_schedule_irq 2、当内核从non-preemptible&#xff08;禁止抢占&#xff09;状态变成pr…...

k8s中,为什么把pod的服务以deployment的形式通过nodeport对外发布,以及容器和虚拟机的一些区别

deployment是个控制器 主要负责管理pod&#xff0c;来代表k8s集群向外提供稳定的服务。 说&#xff0c;k8s有很多优点。 说k8s的优点&#xff0c;可能先需要说容器提供的便利。 同样的硬件资源 跑几个虚拟机&#xff0c;每个虚拟机上跑几个服务。 就挺重了。风扇呼呼叫 …...

PMP--一模--解题--41-50

文章目录 14.敏捷--方法--回顾--回顾是最重要的一个实践&#xff0c;原因是它能让团队学习、改进和调整其过程。41、 [单选] 新项目中的所有团队成员都希望通过尽快交付价值来获得客户的信任。项目经理了解到一个资源已经在其他项目中与发起人一起工作。某资源似乎在使用个人影…...

Kafka启动关闭及其相关命令kafka启动、状态监控、日常操作

开启zookeeper命令(备注:先进入zookeeper的bin目录) ./zkServer.sh start 关闭zookeeper命令(备注:先进入zookeeper的bin目录) ./zkServer.sh stop Kafka启动命令(备注:先进入kafka目录) 常规模式启动kafka bin/kafka-server-start.sh config/server.properties 进程守…...

CentOS 系统设置与维护教程

CentOS 系统设置与维护教程 在这篇博客中&#xff0c;我们将介绍在 CentOS 系统上进行的一些常见操作&#xff0c;包括停止和禁用防火墙服务、配置 SELinux。我们还将展示如何切换到 root 用户以执行这些操作。通过这些操作&#xff0c;你可以更好地管理和配置你的 CentOS 环境…...

流量牵引技术与传统防火墙的区别

在网络安全领域&#xff0c;流量牵引技术和传统防火墙都起着重要的作用&#xff0c;但它们在很多方面存在着明显的区别。 一、工作原理不同 传统防火墙主要是通过设置访问控制规则来过滤网络流量。它基于预先设定的策略&#xff0c;对进入和离开网络的数据包进行检查&#xf…...

【Python爬虫系列】_020.异步协程asyncio

课 程 推 荐我 的 个 人 主 页:👉👉 失心疯的个人主页 👈👈入 门 教 程 推 荐 :👉👉 Python零基础入门教程合集 👈👈虚 拟 环 境 搭 建 :👉👉 Python项目虚拟环境(超详细讲解) 👈👈PyQt5 系 列 教 程:👉👉 Python GUI(PyQt5)文章合集 👈👈…...

ubuntu22安装docker

1、查看服务器系统信息 uname -a&#xff1a;显示内核名称、主机名、内核版本、处理器类型等信息。 lsb_release -a&#xff1a;显示有关 Ubuntu 发行版的详细信息&#xff0c;包括版本号、代号等。 free -h&#xff1a;查看系统内存使用情况。 df -h&#xff1a;查看磁盘空间使…...

【拥抱AI】如何使用Pandas进行数据分段

安装Pandas库 如果您的环境中尚未安装Pandas库&#xff0c;可以使用pip命令进行安装&#xff1a; pip install pandas导入Pandas库 在Python脚本中&#xff0c;导入Pandas库以便使用其数据处理功能&#xff1a; import pandas as pd读取文本文件 使用Pandas的read_csv函数…...

Docker Compose version v2.29.2 提示 exited with code 0 解决方案

问题描述&#xff1a; 使用 docker-compose up 启动容器时&#xff0c;老是报错exited with code 0&#xff0c;容器要么处于退出&#xff0c;要么处于重启阶段&#xff0c;查明原因后&#xff0c;是因为docker容器执行任务完成后就会处于exited状态&#xff0c;必须强制状态。…...

深度学习速通系列:依存分析

依存分析&#xff08;Dependency Parsing&#xff09;是自然语言处理&#xff08;NLP&#xff09;中的一项任务&#xff0c;目的是确定句子中单词之间的依存关系&#xff0c;并将这些关系表示为一个有向图&#xff0c;通常称为依存树。在依存树中&#xff0c;每个节点代表一个单…...

玩转扩展库,温湿度传感器篇!—合宙Air201资产定位模组LuatOS快速入门05

随着LuatOS快速入门系列教程的推出&#xff0c;小伙伴们学习热情高涨。 合宙Air201不仅支持三种定位方式&#xff0c;还具有丰富的扩展功能&#xff0c;通过外扩BTB链接方案&#xff0c;最多可支持21个IO接口&#xff1a;SPI、I2C、UART等多种接口全部支持。 本期&#xff0c…...

【人工智能】人工智能领域中的线性回归算法原理、应用场景及代码示例。

&#x1f3c6;&#x1f3c6;欢迎大家来到我们的天空&#x1f3c6;&#x1f3c6; &#x1f3c6;&#x1f3c6;如果文章内容对您有所触动&#xff0c;别忘了点赞、关注&#xff0c;收藏&#xff01; &#x1f3c6; 作者简介&#xff1a;我们的天空 &#x1f3c6;《头衔》&#x…...

day18JS-微任务、宏任务和node.js

1. 代码的执行流程 代码的执行流程分为同步与异步。 2. 什么样子的是宏任务&#xff1f; 1. setTimeout 和 setInterval 定时器&#xff1a; 没有写时间(传参的)&#xff0c;代表下一帧执行&#xff0c;如果没有其他任务1ms后执行。 // 没有写时间(传参的)&#xff0c;代表下…...