C++相关概念和易错语法(19)(继承规则、继承下的构造和析构、函数隐藏)

news2024/9/21 4:40:13

1.继承规则

继承的本质是复用,是结构上的继承而不是内容上的继承,近似于在子类中声明了父类的成员变量。

(1)写法:class student : public person
     派生类(子类),继承方式,基类(父类) 

它们都分public、protected、private三种,但是含义并不相同。

(2)区分访问限定符和继承方式

访问限定符修饰的是在当前类里面是否可以访问,而继承方式是指在继承的类里面的访问方式,并且一个成员继承后的访问限定符是父类的访问限制符和子类继承方式两者间的较小权限如果A有public成员a,而以protectd继承,因此子类成员的访问限制符是protected。

访问权限由大到小是public>protected>private,protected和private在继承语法外没有区别。比如protected成员public继承后是protected,protected成员private继承后是private,public成员public继承后是public,这都非常容易理解。


#include <iostream>
using namespace std;

class A
{
public:
	int _a = 0;
protected:
	int _b = 1;
private:
	int _c = 2;
};

class B : public A
{
public:
	void GetNum()
	{
		cout << _a << " " << _b  << " " << _d << endl;
	}
	int _d = 3;
};


int main()
{
	B().GetNum();

	return 0;
}

但是要注意private成员无论以何种方式继承,不管是public、protected还是private继承,最终子类成员的访问权限都是private且这个private成员不能被子类使用(包括访问,修改等)

看起来这个成员没有被继承,但实际上子类包含该成员。我们可以从监视窗口和类的大小双重验证这个结论。

(3)在继承里还规定友元关系不能继承,父类的友元不是子类的友元,只有子类自己声明友元

(4)形象理解:父的成员是protected意味着这些成员对外是个秘密,但在自己家庭中不算个秘密,但家庭中的每个人都有义务对外保守这个秘密,即protected无论以何种方式继承对外都不可见。

如果父成员有个private意味着这个成员是自己的秘密,不能给任何人说,比如家里有个隐藏的地下室。但是在继承给自己孩子这个房子时,孩子不知有这个地下室,但这个地下室真实存在,即private无论以何种方式继承对子对外均不可见,但这个private成员真实存在。

父的朋友不是子的朋友,即友元关系不能继承。

(5)protected和private区分

protected在继承以后才有意义。protected和private对外的功能都一样,都是不可见,它们区别在protected对整个继承体系开放(前提没有子类以private继承它),而private是只有该类能访问,其子类也不能访问。

当中途被继承为private后,对子该成员依然不可见。

(6)默认继承方式

我们也可以选择不写继承方式,这个时候class默认私有继承,struct默认以public继承,注意这和默认访问限定符一样,但这是两个概念。

2.继承的函数调用

我们先尝试解读下面的代码,这能帮助我们初步理解继承函数调用的特征:


#include <iostream>
using namespace std;

class A
{
public:
	A(int a, int b, int c)
		:_a(a)
		,_b(b)
		,_c(c)
	{}

	void GetNum()
	{
		printf("%d %d %d\n", _a, _b, _c);
	}

	int _a = 0;
protected:
	int _b = 1;
private:
	int _c = 2;
};

class B : public A
{
public:
	B()
		:A(7, 8, 9)
		, _d(5)
	{}

	void GetNum()
	{
		printf("%d\n", _d);
	}

	int _d = 3;
};

int main()
{
	B().A::GetNum();
	B().GetNum();

	return 0;
}

结果是

(1)构造函数

A作为父类它的构造函数正常写就是了,我们重点关注B的构造函数。

我们知道,构造函数实际构造的顺序是声明的顺序而非初始化列表的顺序。这里我们可以理解为B子类的第一个成员声明是匿名的A,在初始化时要根据A的构造函数把A当作一个整体用匿名初始化对象的方式A(7,8,9)来处理,不能直接对A的成员变量直接初始化继承的A对象和创建一个A对象一样都不会复制函数和static变量,在需要时都是直接去A里面取,即整个继承体系共享一套函数和static变量

(2)函数的隐藏

在继承体系中是允许出现同名函数的,但是这并不构成函数重载,因为函数重载的前提是必须在同一个作用域定义两个同名函数,而这里很明显是在两个类域里定义的同名函数,所以一定不构成重载,而是构成函数的隐藏(只需要同名就构成隐藏了,要和后面的多态区分开)。

如果构成了函数隐藏的话,应该怎么区分调用这两个函数呢?很多人会以为能像函数重载的调用那样根据参数匹配程度来调用,但这个逻辑在继承里走不通,万一我就想利用隐式类型转换调用子的成员函数,但是参数却匹配了父的成员函数呢?万一是我参数写错了导致匹配错误的情况呢?这些问题都会导致歧义的发生。所以规定,只要构成函数隐藏,调用父类一定要指明类域,调用当前类的不用指明,如果不指明类域,一律只按调用当前类的函数来处理,就算此时调用匹配父类的函数参数。

我们这里还可以学到,虽然我们不能直接访问父类的私有成员_c == 9,但是我们可以选择调用父类的非private和protected函数,从而达到间接访问的目的。

(3)析构函数与构造函数

析构函数的理解相对复杂,我们先看一下下面的代码,这能帮助我们深入理解构造和析构函数:


#include <iostream>
using namespace std;


class A
{
public:
	~A()
	{
		delete _a;
	}
	int* _a = new int(1);
};

class B : public A
{
public:
	B()
		:_b(_a)
	{}

	~B()
	{
		A::~A();

		*_b = 2;
	}

	int* _b;
};

int main()
{
	B a;

	return 0;
}

结果是报错,接下来我会详细分析里面的代码

为什么写作A::~A();

析构函数的名字会被统一处理为destructor(),父子类的析构函数同名构成了函数隐藏,因此想要调用父的析构函数,就必须要指定类域,这个底层细节需要我们记住。这个特殊处理的原因在多态我会提及。

为什么报错?

由于在父类的析构函数调用后间接使用了父类的成员,所以出现越界访问的情况。

我们还能发现B的构造函数借助了A的成员变量。这里要区分开的是,在子类初始化父类时只能以父类为整体去调用它的构造函数如A(7,8,9),而本质上继承的只是它的成员变量,在子类的构造函数中可以直接用父类的成员。

如此一来,我们能发现,子类的构造函数可能会依靠父类的成员变量,若父类先析构,那在子类需要用到父类成员变量时就可能会发生越界访问。所以规定,父类的构造函数一定先于子类,父类的析构一定晚于子类(构造先父后子,析构先子后父)。

在实现层面上,由于子类的第一个成员变量默认是父类A,所以我们可以不用关心,毕竟初始化顺序是按声明而不是初始化列表。但是我们要避免显式地调用父类的析构,即A::~A()不要出现在我们的继承代码中。当子类的析构函数走完后,会去自动调用父类的析构。同理,如果没有显式写构造,编译器也会首先自动调用父类的默认构造。

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

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

相关文章

Cypress UI自动化之安装环境

注&#xff1a;macOS系统 一、git环境 略 二、node环境 1、安装nvm 前提&#xff1a;有装过Homebrew&#xff0c;参考adb使用方法文档 1、安装nvm&#xff1a;首先要保证之前没有安装过node&#xff0c;如果之前安装过&#xff0c;先 brew uninstall node brew install n…

省市县下拉框的逻辑以及多表联查的实例

2024.7.12 一. 省市县的逻辑开发。1、准备&#xff1a;1.1. 要求&#xff1a;1.2 数据库表&#xff1a; 2. 逻辑&#xff1a;3. 方法3.1 创建实体类3.2 数据访问层3.3 实现递归方法3.4 控制器实现3.5 前端处理 二、多表联查&#xff08;给我干红温了&#xff09;1. 出现了问题2…

Java性能优化-switch性能优化-用String还是int做比较

场景 Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化&#xff1a; Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化_java热点函数-CSDN博客 参考以上性能测试工具的使用。 下面针对Java中对switch-case比较时…

LLM 合成数据生成完整指南

大型语言模型是强大的工具&#xff0c;不仅可以生成类似人类的文本&#xff0c;还可以创建高质量的合成数据。这种能力正在改变我们进行 AI 开发的方式&#xff0c;特别是在现实世界数据稀缺、昂贵或隐私敏感的情况下。在本综合指南中&#xff0c;我们将探索 LLM 驱动的合成数据…

访问控制的定义与原理

访问控制(Access Control)是一种重要的安全机制&#xff0c;用于限制对程序中的数据、函数、类以及计算机系统中资源(如文件、数据库、网络设备等)的访问权限。其主要目的是保护系统中的敏感信息和资源&#xff0c;防止未经授权的访问和操作&#xff0c;确保系统的安全性、完整…

无向图的双连通分量——AcWing 395. 冗余路径

无向图的双连通分量 定义 在无向图中&#xff0c;一个双连通分量&#xff08;Biconnected Component, BCC&#xff09;是指这样的子图&#xff1a;删除其中任意一个顶点都不会使这个子图分离成两个或更多个不相连的子图。换句话说&#xff0c;双连通分量是无割点的极大连通子…

lua 脚本语言 : 基础到高级语法

❃博主首页 &#xff1a; 「码到三十五」 &#xff0c;同名公众号 :「码到三十五」&#xff0c;wx号 : 「liwu0213」 ☠博主专栏 &#xff1a; <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 &#xff1a…

[Python学习篇] Python异常

什么是异常&#xff1f; 异常&#xff08;Exception&#xff09;是指在程序执行过程中发生的错误事件&#xff0c;它会中断程序的正常执行流程。异常可以由程序中的错误引发&#xff0c;也可以通过主动抛出异常来处理特殊情况。Python 使用异常处理机制来捕获和处理这些错误&am…

初识c++(构造函数,析构函数,拷贝构造函数,赋值运算符重载)

一、类的默认函数 默认成员函数就是用户没有显式实现&#xff0c;编译器会自动生成的成员函数称为默认成员函数。 #include<iostream> using namespace std; class Date { public:Date(){_year 1;_month 1;_day 1;cout << _year << "/" <&…

日常的学习

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a;Android ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 正文 7.11 resAndroidManifest 笔记 <> <> selector shape resources main下的AndroidMainifest.xml文件 application …

sql注入时间盲注

基于时间的盲注 也叫延时注入。通过观察页面&#xff0c;既没有回显数据库内容&#xff0c;又没有报错信息也没有布尔类型状态&#xff0c;那么我们可以考虑用“绝招”--延时注入。延时注入就是根据页面的响应时间来判断是否存在注入&#xff0c;一点一点注入出数据库的信息。我…

【进阶】利用python内置模块自动化发送邮件及邮件附件

目录 自动化发送邮件 流程&#xff1a; 步骤&#xff1a; 【重点】 【MIMEText--发送文本类型的邮件】 【MIMEImage-发送附件为图片的邮件】 【MIMEBase--发送附件为html报告的邮件】 自动化发送邮件 以qq邮箱为例&#xff0c;提前打开POP3/IMAP/SMTP/Exchange/CardDAV 服…

【web]-信息收集-空白页面

打开是一张图 查看源码&#xff0c;发现就一个链接是有用信息&#xff0c;用目录扫描工具&#xff0c;没有发现有价值的信息。 F12&#xff0c;查看请求和相应信息&#xff0c;在响应头中发现了信息。 还有一个小技巧&#xff1a;点击手机图标&#xff0c;可以切换到手机模式中…

Web浏览器485通讯读取RFID卡号js JavaScript

本示例使用设备&#xff1a;485通讯液显带键盘RFID打菲计件读卡器工位机串口可二次开发编程-淘宝网 (taobao.com) <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> …

计组_总线

2024.06.21&#xff1a;计算机组成原理总线学习笔记 第23节 总线 3.1 总线的基本概念&#xff08;联想数据通路&#xff09;3.2 总线的分类3.2.1 片内总线&#xff08;CPU芯片内部的总线&#xff09;3.2.2 系统总线3.2.3 通信总线&#xff08;跨系统&#xff0c;408一般不考&am…

四个“一体化”——构建数智融合时代下的一站式大数据平台

随着智能化技术的飞速发展&#xff0c;尤其是以生成式AI为代表的技术快速应用&#xff0c;推动了数据与智能的深化融合&#xff0c;给数据基础设施带来了新的变革和挑战。如何简化日益复杂的系统架构&#xff0c;提高数据处理效率&#xff0c;降低开发运维成本&#xff0c;促进…

十、(正点原子)Linux阻塞和非阻塞IO

阻塞和非阻塞 IO 是 Linux 驱动开发里面很常见的两种设备访问模式&#xff0c;在编写驱动的时候一定要考虑到阻塞和非阻塞。这里的“IO”并不是我们学习 STM32 或者其他单片机的时候所说的“GPIO”(也就是引脚)。这里的 IO 指的是 Input/Output&#xff0c;也就是输入/输出&…

matlab支持向量机使用错误

&#x1f3c6;本文收录于《CSDN问答解答》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收藏&…

使用Qt和mitmproxy开发一个抓取网页短视频的万能工具

目录 实现原理 mitmproxy介绍 功能简介 安装 脚本示例 如何使用 解释 注意事项 QT工具实现 其他资源 实现原理 使用WebView组件造一工具,工具可输入网页地址并显示网页内容及播放视频。把工具的代理设置指向mitmproxy的端口服务。配合使用mitmproxy的MITM技术,监…

MySql性能调优03-[SQL优化]

SQL优化 MySQL优化SQL优化-不要写select *SQL优化-小表驱动大表&#xff0c;而不是大表驱动小表SQL优化-连接查询代替子查询SQL优化-提升group by的效率 MySQL优化 trace工具 set session optimizer_traceenabledon,end_markers_in_json on; -- 开启trace select * From emplo…