C++进阶中继承的全部主要内容

news2024/11/25 20:33:20

小编在学习完C++中继承的内容之后,觉得继承的内容很重要,所以今天给大家带来的内容就是继承的主要内容,今天的内容包括继承的定义与语法继承方式与权限基类和派生类的类型转换赋值继承中的隐藏继承中析构函数的特别之处多继承和虚拟继承对象组合和继承的区别。好啦,这就是今天的全部内容,该学习啦!~~~

一、继承的基础

1、继承的定义与语法

在了解继承之前先看看继承的定义:

继承 (inheritance) 机制是面向对象程序设计 使代码可以复用 的最重要的手段,它允许程序员在
持原有类特性的基础上进行扩展 ,增加功能,这样产生新的类,称派生类。继承 呈现了面向对象
程序设计的层次结构 ,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,
承是类设计层次的复用
在了解了继承的定义之后,我们现在看一段代码及注释,来学习继承的语法:
class Person
{
public:
 void Print()
 {
 cout << "name:" << _name << endl;
 cout << "age:" << _age << endl;
 }
protected:
 string _name = "peter"; // 姓名
 int _age = 18;  // 年龄
};
//语法   子类(派生类) + : + 继承方式 + 父类(基类) 
class Student : public Person
{
protected:
 int _stuid; // 学号
};

语法图(帮助大家更加深刻理解继承的语法):

2、继承的语法与权限

继承类型有三种   1、public 继承 2、protected 继承 3、private 继承 , 在继承中 public > protected > private 的权限

// 1、public 继承   将父类(基类)所有部分都继承下来,但是 private 虽然会被继承下来 但是在类外面不可以去被访问,protected的部分可以在父类和基类中使用,但是不可以在类外使用,private 部分只能在父类中使用
// 如果在子类中(派生类)要访问父类(基类)的私有成员变量,就不能定义 private,应该定义为 protected 部分,并且要使用 public继承方式 或者 protected继承方式
// 2、protected 继承 将父类(基类)中的所有部分都继承下来 但是继承下来的public部分的权限被缩小到 protected 其他不变
// 3、private 继承 将父类(基类)的所有部分你都继承下来 但所有继承下来的部分权限都是private,都只可以在父类(基类)中使用,不可以在类外使用(对外不可见)
// 注意:
// 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡
// 使用protetced / private继承,因为protetced / private继承下来的成员都只能在派生类的类里面使用

3、基类和派生类的类型转换赋值和隐藏,把隐藏放到这块讲解

为了明白类型转换赋值和隐藏,大家先看下面一段代码及其注释,来理解一下这两个新的概念是啥:

// 一般两个类不定义相同的函数或者类成员函数,当然也会有特殊情况
// 如果定义了相同名字的函数或者类成员函数,那就先在自己的域中寻找,如果找不到在去父类(基类)中去寻找
// 两个函数名相同的话被称为隐藏
class A
{
public:
	A(const int& x = 1)
		:num(x)
	{}
	void Print()
	{
		cout << num << endl;
	}
private:
	int num;
};

class B : public A
{
public:
	// 默认构造
	B(const int num = 0, string name = "张三", string tel = "110")
		:_num(num)
		, _name(name)
		, _tel(tel)
		, A(num)  // 把从A中继承到的部分看作一个自定义类型成员 
	{}	          // 初始化链表的部分是从先定义的部分开始初始化,和写的先后顺序无关
	void Print()  // 初始化部分必须先初始化父类(基类)部分(编译器默认初始化顺序) 因为为了后面初始化派生类(子类)部分时使用父类进行初始化
	{
		cout << _num << endl;
	}

private:
	int _num = 0;
	string _name;
	string _tel;
};

void test2()
{
	B aa;
	// 隐藏之后如果非得要调用A中的Print()那就需要类引用操作符
	aa.Print();
	aa.A::Print();   // 这样就可以实现啦

	B b1;
	A a1;
	// 这里有一个 类型转换赋值 操作,因为定义继承只有,B 定义的部分多了 A 中的成员
	// 就会有这个操作
	a1 = b1;   // 类型转换赋值 -> 切割,切片(把b1和自己相同的部分直接赋值给自己,中间不存在临时变量)
	a1.Print();
}

隐藏:

从上面的代码中可以看出,子类和父类中都有Print()函数,在子类和父类中两个函数的名字相同就会构成隐藏,这里只是函数名相同,一般在调用这个函数之后,会现在自己的类中寻找,找不到才会去父类中寻找。后面学到多态之后,大家还会学到一个叫虚函数的东西(函数名,返回值,函数参数)都要相同才可以构成虚函数。

类型转换赋值:

从上面的代码可以看出,父类中有一个_num私有成员变量,子类中有 _num ,  _name , _tel 三个私有成员变量,这时候会存在一个特殊的转换方式,在强制转换的时候中间不会产生临时变量

操作方式:

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用 。这里有个形象的说法叫切片
或者切割。寓意把派生类中父类那部分切来赋值过去。
具体编译器的操作过程如下图:
二、继承的强化
1、继承中析构函数的特别之处
在了解继承中的析构函数之前,大家先看下面一段代码及注释,可以让大家了解到继承中的默认构造,赋值构造和拷贝构造与之前学到的有什么区别:
class Name
{
public:
	// 默认构造
	Name(const string& x)
		:_name(x)
	{
		cout << "Name 默认构造" << endl;
	}
	Name& operator=(const Name& x)
	{
		_name = x._name;
		return *this;
	}
	~Name()
	{
		_name = "";
		cout << "~Name 析构函数" << endl;
	}

protected:
	string _name;
};

class Num : public Name
{
public:
	// 默认构造
	Num(const int& num = 0,const string& name = "张三")
		:_num(num)
		,Name("name")
	{
		cout << "Num 默认构造" << endl;
	}
	// 拷贝构造
	Num(const Num& tem,const string& name)
		:_num(tem._num)
		,Name("name")
	{
		cout << "Num 拷贝构造" << endl;
	}
	// 赋值构造
	Num& operator=(const Num& x)
	{
		Name(x._name); // 在进行赋值构造的时候还要对父类进行父类的构造
		_num = x._num;
		return *this;
	}
	~Num()
	{
		// 调用析构函数的时候应该要先析构子类(派生类) 然后在析构基类(父类)
		// 如果先析构基类,如果后面子类仍然使用基类的成员就会出现越界访问
		// 由于多态,析构函数的名字会被统一处理成 destructor()
		// 父类析构不用自己调用,编译器会自己调用
		//Name::~Name();
		_name = "";
		_num = 0;
		cout << "~Num() 析构函数" << endl;
		// 派生类(子类)析构完之后编译器会自己调用父类(基类)的析构函数 减少了我们的调用过程
	}

protected:
	int _num;
};

分析一下,在对子类对象进行构造的时候必须考虑到父类成员,在构造子类对象时我们必须手动写上对父类对象的构造,但是这里的析构函数可不一样,为了了解到有什么不同之处,请大家看下面代码及运行结果:

大家可以看到我 上面的子类析构函数的代码中没有调用父类的析构函数,但 运行结果却调用了两次父类的析构函数,这就是析构函数和构造函数的不同之处,调用子类析构函数, 在子类析构函数结束的时候编译器会自己调用父类的析构函数,帮助程序员清除所有的内容。
还有一个重要的一点:
在继承的过程中, 每个函数的析构函数名字其实都为destructor(),这是为了在后面的多态内容学习中,可以实现调用谁就实现谁的操作,在这里我们不做太多的讲解。
二、多继承和虚拟继承
1、为了知道多继承是什么,接下来大家先看一段代码及注释来明白:
// 多继承 : 一个子类有两个或以上直接父类时称这个继承关系为多继承
class Student
{
public:
	//.....
private:
	string _name;
};

class Teacher
{
public:
	//......
private:
	string _id;
};

class Assistant : public Student, public Teacher
{
public:
	//....
private:
	int _num;
};

大家看完肯定会有点懵懵的,在这里我为大家准备了一幅图来说明多继承

2、虚拟继承

在了解虚拟继承时,大家先看下面一段代码及注释来明白为什么要出现虚拟继承这个概念:

class P
{
public:

protected:
	string _name; // 这里有一个名字
	string _address;
	string _gender;
};

// 对于相同的父类(基类)内容使用虚拟继承
class st :  public P
{
public:

protected:
	string _id;
	string _name = "bingbing"; // 这里也有一个名字
};

// 同上
class te :  public P
{
public:

protected:
	string _name = "lisi"; // 这里也有一个名字
	string _id;
};

从上面代码可以看出,st的类继承了P,te的类也继承了P,但是在继承的过程中,P类中有一个_name ,st类中也有_name,在继承完P之后st类中就有两个名字,te类和st类相同,正常情况下我们需要一个名字就好啦,这里就构成了数据冗余和二义性,为了解决这种情况,C++提出了虚拟继承的概念,语法就是在继承的时候加上 virtual 语法图如下:

语法就这么简单,虚拟继承之后就只会有一个 _name,对父类的_name 进行修改,就会修改每个虚拟继承处的_name

三、继承与对象结合的区别

public继承是一种is-a 的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a 的关系。假设 B 组合了 A ,每个 B 对象中都有一个 A 对象。
优先使用对象组合,而不是类继承 (重点记住)。
继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
白箱复用 (white-box reuse) 。术语 白箱 是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。
对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
(black-box reuse) 因为对象的内部细节是不可见的。对象只以 黑箱 的形式出现
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。
总结一下就是 能使用对象结合的地方就是用结合即可。
好啦好啦,这就是今天的全部内容,希望大家看完之后会有收获!~~~~

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

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

相关文章

数据跨境法案:美国篇下

数据跨境法案美国篇下主要介绍2018年《出口管制改革法案》(ECRA)、2018 年《外国投资风险审查现代化法案》(FIRRMA) 和《强化海外关键出口国家框架法》&#xff08;ENFORCE&#xff09;。其中 ECRA法案和 FIRRM 法案作为2019财年约翰S麦凯恩国家国防授权法&#xff08;NDAA&…

MySQL定时备份数据,并上传到oss

1.环境准备 1.安装阿里云的ossutil 2.安装mysql 2.编写脚本 脚本内容如下 #!/bin/bash # 数据库的配置信息&#xff0c;根据自己的情况进行填写 db_hostlocalhost db_usernameroot db_passwordroot db_namedb_root # oss 存贮数据的bucket地址 bucket_namerbsy-backup-buck…

开源发票识别InvoiceNet项目Windows部署踩坑记(1)

今天安装在github上的InvoiceNet开源项目&#xff0c;准备对它进行测试&#xff0c;安装过程出现了一些问题&#xff0c;做个记录&#xff0c;给遇到兄弟爬坑。 第一个问题&#xff0c;conda的问题&#xff0c; 这是另外一个包管理器&#xff0c;不仅仅可以管理python的虚拟…

如何通过smtp设置使ONLYOFFICE协作空间服务器可以发送注册邀请邮件

什么是ONLYOFFICE协作空间 ONLYOFFICE协作空间&#xff0c;是Ascensio System SIA公司出品的&#xff0c;基于Web的&#xff0c;开源的&#xff0c;跨平台的&#xff0c;在线文档编辑和协作的解决方案。在线Office包含了最基本的办公三件套&#xff1a;文档编辑器、幻灯片编辑…

XXE:XML外部实体引入

XXE漏洞 如果服务器没有对客户端的xml数据进行限制&#xff0c;且版本较低的情况下&#xff0c;就可能会产生xxe漏洞 漏洞利用流程 1.客户端发送xml文件&#xff0c;其中dtd存在恶意的外部实体引用 2.服务器进行解析 3.服务器返回实体引用内容 危害&#xff1a;任意文件读…

【C语言】 约瑟夫环,循环链表实现

1、循环链表实现约瑟夫环&#xff0c;每次经过特定步数删除一个元素 //looplist.h #ifndef LOOPLIST_H #define LOOPLIST_H #include<stdio.h> #include<string.h> #include<stdlib.h>typedef int datatype;typedef struct Node {union {int len;datatype d…

大模型应用—大模型赋能网络爬虫

大模型赋能网络爬虫 简单来说,网页抓取就是从网站抓取数据和内容,然后将这些数据保存为XML、Excel或SQL格式。除了用于生成潜在客户、监控竞争对手和市场研究外,网页抓取工具还可以用于自动化你的数据收集过程。 借助AI网页抓取工具,可以解决手动或纯基于代码的抓取工具的…

花几千上万学习Java,真没必要!(二十二)

1、final关键字&#xff1a; 测试代码1&#xff1a; package finaltest.com;public class FinalBasicDemo {public static void main(String[] args) {// final修饰基本数据类型变量final int number 5;// 尝试修改number的值&#xff0c;这将导致编译错误// number 10; // …

JAVA在线文档

1.存在码 JDK21中文API 2.全栈行动派 JDK17中文API 3.mklab.cn JDK11中文API JDK8中文API JDK7-21英文API 4.docs.oracle.com JDK7-22英文文档

Python实现文本转换为语音

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…

探究Type-C接口的安装方式及应用场景

在现代电子设备中&#xff0c;Type-C接口因其高速传输、可逆插拔等特点&#xff0c;越来越受到用户的青睐。而Type-C接口的安装方式也是至关重要的一环&#xff0c;影响着设备的稳定性和可靠性。本文将深入分析Type-C接口的四种主要安装方式&#xff1a;表面贴片、穿孔焊接、夹…

【C++】内存管理的深度解析与实例

C内存管理的深度解析与实例 一、C内存管理的基本概念二、C内存分配方式1. 静态内存分配2. 动态内存分配 三、C内存管理的常见问题及解决策略1. 内存泄漏2. 堆内存碎片化3. 栈溢出 四、C内存管理的最佳实践1. 使用RAII&#xff08;Resource Acquisition Is Initialization&#…

图片转pdf的软件有哪些?这几种转换工具了解下

在日常的办公学习中&#xff0c;图片转PDF的需求愈发普遍。不论是工作汇报、学习笔记还是生活点滴&#xff0c;我们都希望将重要的图片内容整理成易于查阅的PDF格式。那么&#xff0c;有哪些软件可以做到将图片转换成PDF格式呢&#xff1f;给大家介绍5种简单好用的转换方法&…

昇思25天学习打卡营第17天|CycleGAN图像风格迁移互换

CycleGAN是一种非常酷的人工智能模型&#xff0c;它能够在没有成对训练样本的情况下&#xff0c;将一种图像风格迁移到另一种图像上。比如&#xff0c;你可以把苹果的图片变成橘子的风格&#xff0c;反之亦然。 CycleGAN的网络结构&#xff0c;包括生成器和判别器&#xff0c;它…

英语语法第八课副词

文章目录 1、副词分类1.1 时间副词&#xff0c;表示时间或频率1.2 地点副词&#xff0c;表示地点或位置1.3 方式副词&#xff0c;表示行为方式1.4 程度副词&#xff0c;表示动作程度1.5 疑问副词&#xff0c;引导特殊疑问句1.6 强调副词&#xff0c;强调形容词或动词1.7 连接副…

RabbitMQ的学习和模拟实现|sqlite轻量级数据库的介绍和简单使用

SQLite3 项目仓库&#xff1a;https://github.com/ffengc/HareMQ SQLite3 什么是SQLite为什么需要用SQLite官方文档封装Helper进行一些实验 什么是SQLite SQLite是一个进程内的轻量级数据库&#xff0c;它实现了自给自足的、无服务器的、零配置的、事务性的 SQL数据库引擎…

Linux shell编程学习笔记65: nice命令 显示和调整进程优先级

0 前言 我们前面学习了Linux命令ps和top&#xff0c;命令的返回信息中包括优先序&#xff08;NI&#xff0c;nice&#xff09; &#xff0c;我们可以使用nice命令来设置进程优先级。 1 nice命令 的功能、格式和选项说明 1.1 nice命令 的功能 nice命令的功能是用于调整进程的…

ARM 单片机裸机任务调度框架

前言&#xff1a; 在没有使用操作系统的情况下&#xff0c;一个合理的裸机任务调度方式&#xff0c;可以更好的提供数据的处理&#xff0c;和用户体验&#xff0c;有多种任务调度的方式。 方案 1&#xff1a; 从上到下的任务调度方式&#xff0c;C语言程序的代码是在main函数…

重生之我在学数据结构——队列

一.队列的概念 队列是一种先进先出(First In First Out &#xff0c;FIFO)的数据结构&#xff0c;可以简单理解为排队的概念。在队列中&#xff0c;数据项按照插入的顺序排列&#xff0c;并且只能在队列的一端插入&#xff08;称为队尾&#xff09;&#xff0c;在另一端删除&a…

Vue项目实现单点登录(SSO)的逻辑和基本流程

项目集群如果一个一个登录会非常麻烦&#xff0c;尤其是一些大企业或者多项目使用一套体系数据的环境中&#xff0c;这时候登录中心也就应用而生&#xff0c; 今天简单说一下vue sso的实现思路&#xff0c;vue项目实现单点登录&#xff08;SSO&#xff09;通常涉及以下几个步骤…