深入挖掘C++中的特性之一 — 继承

news2024/11/22 10:32:27

目录

1.继承的概念

2.举个继承的例子

3.继承基类成员访问方式的变化

1.父类成员的访问限定符对在子类中访问父类成员的影响

2.父类成员的访问限定符+子类的继承方式对在两个类外访问子类中父类成员的影响

4.继承类模版(注意事项)

5.父类与子类间的转换

6.继承中的作用域(主讲隐藏)

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

1.子类中的构造函数

2.子类中的拷贝构造函数

3.子类中的赋值运算符重载

4.子类中的析构函数

8.不能被继承的类

9.继承与友元

10.继承与静态成员

11.多继承及其菱形继承问题

0.简单介绍

1.单继承模型

2.多继承模型

3.菱形继承模型

4.二义性例子

5.虚继承

12.继承和组合


1.继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许我们在保持原有类特性的基础上进⾏扩展,增加⽅法(成员函数)和属性(成员变量),这样产⽣新的类,称派⽣类。继承 呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的函数层次的 复用,继承是类设计层次的复用。

2.举个继承的例子

下面我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复用这些成员,就不需要重复定义了,省去了很多麻烦。

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; // 职称 
};

3.继承基类成员访问方式的变化

1.父类成员的访问限定符对在子类中访问父类成员的影响

1.父类成员public、protected修饰:子类中可以访问父类的成员变量。

2.父类成员private修饰:子类中不可以访问父类的成员变量。

2.父类成员的访问限定符+子类的继承方式对在两个类外访问子类中父类成员的影响

1.private继承:在两个类外访问不了。

2.除了private继承以外的其它继承方式:

父类成员在子类中的访问方式==min(成员在父类的访问限定符,继承方式),其中public>

protected>private。

4.继承类模版(注意事项)

namespace yx
{


	//用vector容器实现stack容器适配器
	template<class T>
	class stack : public std::vector<T>
	{
	public:
		void push(const T& x)
		{
			// 基类是类模板时,需要指定⼀下类域, 
			// 否则编译报错:error C3861: “push_back”: 找不到标识符 
			
			// 因为stack<int>实例化时,也实例化vector<int>了 
			
			// 但是模版是按需实例化,虽实例化vector<int>了,
			// 但vector<int>中的push_back等成员函数未实例化,所以找不到 
			vector<T>::push_back(x);

			//push_back(x);需要指定类域实例化模版参数
		}
		void pop()
		{
			vector<T>::pop_back();
		}
		const T& top()
		{
			return vector<T>::back();
		}
		bool empty()
		{
			return vector<T>::empty();
		}
	};


}

5.父类与子类间的转换

class Person
{
protected:
	string _name; // 姓名 
	string _sex; // 性别 
	int _age; // 年龄 
};


class Student : public Person
{
public:
	int _No; // 学号 
};

1.public继承的派生类对象、指针、引用可以赋值给基类的对象、指针、引用;(切片)(基类指针或引用指向的是派生类中切出来的基类那部分)

2.基类对象不能赋值给派生类对象;

3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。

Student sobj;
// 1.派⽣类对象、指针、引用可以赋值给基类的对象、指针、引⽤ 
Person pobj = sobj;
Person* pp = &sobj;
Person& rp = sobj;
//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错 
sobj = pobj;
//3.可以这么写
Student sobj;
Person* pp = &sobj;
Person& rp = sobj;
Student* sp = (Student*)pp;
Student& rs = (Student&)rp;

6.继承中的作用域(主讲隐藏)

// 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;
}

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

在讨论子类的默认成员函数时,我们只需要按照之前看默认成员函数的方式+将父类中的那些成员看作是子类中的一个对象来看就可以。

1.子类中的构造函数

class Person
{
public:
	Person(const char* name)
		: _name(name)
	{
		cout << "Person()" << endl;
	}

	
protected:
	string _name; // 姓名
};



class Student : public Person
{
public:
	// 子类中默认生成的构造函数的行为
	// 1、内置类型->不确定
	// 2、自定义类型->调用默认构造
	// 3、继承的父类成员看做一个整体对象,要求调用父类的默认构造

    //自己写的子类构造
	Student(const char* name, int num, const char* addrss)
		:Person(name)//必须显式调用父类的构造函数
		, _num(num)
		, _addrss(addrss)
	{}

protected:
	int _num = 1; //学号
	string _addrss = "武汉市洪山区";
};

2.子类中的拷贝构造函数

class Person
{
public:

	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}

	
protected:
	string _name; // 姓名
};


class Student : public Person
{
public:
	
	// 严格说Student拷贝构造默认生成的就够用了
	// 如果有需要深拷贝的资源,才需要自己实现

	Student(const Student& s)//注意:这里写成了初始化列表的方式,
//但请不要混淆(这里并不是初始化的意思,
//但如果写成了初始化列表的方式,这里没有写Person(s)的话,将会调用Person类的默认构造函数)

		:Person(s)//必须显式调用父类的拷贝构造函数
		, _num(s._num)
		, _addrss(s._addrss)
	{
		// 深拷贝
	}

	
protected:
	int _num = 1; //学号
	string _addrss = "武汉市洪山区";
};

3.子类中的赋值运算符重载

class Person
{
public:
	

	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}

	
protected:
	string _name; // 姓名
};


class Student : public Person
{
public:
	

	// 严格说Student赋值重载默认生成的就够用了
	// 如果有需要深拷贝的资源,才需要自己实现
	Student& operator=(const Student& s)
	{
		if (this != &s)
		{
			// (注意)规定:父类和子类的operator=构成隐藏关系,故这里需要指定类域。
			Person::operator=(s);//这里要显式调用父类的operator=()函数
            //这里的s变量传给父类的函数,用到了刚才讲的父类与子类间的转换的知识

			_num = s._num;
			_addrss = s._addrss;
		}

		return *this;
	}

	
protected:
	int _num = 1; //学号
	string _addrss = "武汉市洪山区";
};

4.子类中的析构函数

class Person
{
public:
	
	~Person()
	{
		cout << "~Person()" << endl;
	}

protected:
	string _name; // 姓名
};


class Student : public Person
{
public:
	

	// 严格说Student析构默认生成的就够用了
	// 如果有需要显示释放的资源,才需要自己实现
	// 析构函数都会被特殊处理成destructor() 
	~Student()
	{
		_addrss.~string();

		// (注意)规定:子类的析构和父类析构函数也构成隐藏关系
		// 规定:子类中不需要显示调用父类的析构,子类析构函数之后,会自动调用父类析构
		// 这样保证析构顺序,先子后父,显示调用取决于实现的人,不能保证先子后父
		// 先子后父
		//Person::~Person();//指定类域(因为隐藏)
		
	}
protected:
	int _num = 1; //学号
	string _addrss = "国庆快乐! 祖国万岁!";
};

8.不能被继承的类

C++11新增了⼀个final关键字,final修改基类,派生类就不能继承了。

// C++11的⽅法 
class Base final
{
public:
	void func5() { cout << "Base::func5" << endl; }
protected:
	int a = 1;
};

9.继承与友元

友元关系不能继承到子类。

10.继承与静态成员

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

11.多继承及其菱形继承问题

0.简单介绍

单继承:⼀个派⽣类只有⼀个直接基类时称这个继承关系为单继承

多继承:⼀个派⽣类有两个或以上直接基类时称这个继承关系为多继承,多继承对象在内存中的模型是,先继承的基类在前⾯,后⾯继承的基类在后⾯,派⽣类成员在放到最后⾯。

菱形继承:菱形继承是多继承的⼀种特殊情况。菱形继承的问题,从下⾯的对象成员模型构造,可以看出菱形继承有数据冗余和⼆义性的问题,在下图的Assistant的对象中Person成员会有两份。⽀持多继承就可能会有菱形继承,像Java就直接不⽀持多继承,规避掉了这⾥的问题,所以实践中我们也是不建议设计出菱形继承这样的模型的。

1.单继承模型

2.多继承模型

3.菱形继承模型

4.二义性例子

class Person
{
public:
	string _name; // 姓名 
};


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


class Teacher : public Person
{
protected:
	int _id; // 职⼯编号 
};


class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程 
};


int main()
{
	// 编译报错:error C2385: 对“_name”的访问不明确 
	Assistant a;
	//a._name = "peter";会报错


	// 需要显⽰指定访问哪个基类的成员可以解决⼆义性问题,但是数据冗余问题⽆法解决 
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
	return 0;
}

5.虚继承

class Person
{
public:
	string _name; // 姓名 
};


// 使⽤虚继承Person类 
class Student : virtual public Person
{
protected:
	int _num; //学号 
};


// 使⽤虚继承Person类 
class Teacher : virtual public Person
{
protected:
	int _id; // 职⼯编号 
};


// 教授助理 
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程 
};


int main()
{
	// 使⽤虚继承,可以解决数据冗余和⼆义性 
	Assistant a;
	a._name = "peter";
	return 0;
}

举个例子:

注意:谁有数据冗余和二义性,就在继承它的时候加上virtual,使用虚继承,可以解决数据冗余和⼆义性

由上图可以看出A有数据冗余和二义性,所以就在继承A的位置B和C加上virtual

12.继承和组合

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

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

相关文章

FTP服务原理及使用

一、配置FTP服务 配置FTP服务&#xff0c;内容不难&#xff0c;本地虚拟机上自带FTP服务程序&#xff0c;自己下载即可。 二、理解FTP的主动模式和被动模式原理 1 主动模式 客户端先开启一个大于1024的随机端口&#xff0c;用来与服务器的21号端口建立控制连接&#xff1b;当…

rabbitmq消费者应答模式

1.应答模式 RabbitMQ 中的消息应答模式主要包括两种&#xff1a;自动应答&#xff08;Automatic Acknowledgement&#xff09;和手动应答&#xff08;Manual Acknowledgement&#xff09;。 自动应答&#xff1a; 不在乎消费者对消息处理是否成功&#xff0c;都会告诉队列删…

win11远程连接MySQL(linux版),不需安装docker容器

不想安装虚拟机&#xff0c;想在Windows 11上运行Linux。 在win11的搜索框内&#xff0c;搜索"启用或关闭"&#xff0c;出现了“启用或关闭Windows功能”&#xff0c;双击打开。 勾选"适用于Linux的Windows子系统"&#xff0c;“虚拟机平台”&#xff0c…

网站可疑问题

目标站点 Google hack 页面访问 抓包 POST /admin.php?actionlogin HTTP/2 Host: www.xjy.edu.cn Cookie: xkm_sidA6x4Cgw2zx User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:130.0) Gecko/20100101 Firefox/130.0 Accept: text/html,application/xhtmlxml,appl…

AtCoder ABC373 A-D题解

ABC372 的题解没写是因为 D 是单调栈我不会(⊙︿⊙) 比赛链接:ABC373 总结&#xff1a;wssb。听说 E 很水&#xff1f;有时间我看看。 Problem A: Code #include <bits/stdc.h> using namespace std; int mian(){int ans0;for(int i1;i<12;i){string S;cin>&g…

7c结构体

文章目录 一、结构体的设计二、结构体变量的初始化2.1结构体在内存表示&#xff1b;**2.2**结构体类型声明和 结构体变量的定义和初始化只声明结构体类型声明类型的同时定义变量p1用已有结构体类型定义结构体变量p2*定义变量的同时赋初值。*匿名声明结构体类型 2.3 结构体嵌套及…

【笔记】选择题笔记408

无向图有16条边&#xff0c;其中度为4的顶点个数为3&#xff0c;度为3的顶点个数为4&#xff0c;其他顶点的度均小于3。图G所含的顶点个数至少是&#xff1a;11 总度数16232 度为2的顶点个数为x&#xff0c;度为1的顶点个数为y&#xff0c;度为0的顶点个数为z 由此可列出三元一…

工程机械车辆挖掘机自卸卡车轮式装载机检测数据集VOC+YOLO格式2644张3类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;2644 标注数量(xml文件个数)&#xff1a;2644 标注数量(txt文件个数)&#xff1a;2644 标注…

从零开始Hadoop集群环境搭建

目录 1. Centos7.5硬件配置1.1 创建虚拟机1.2 虚拟机系统设置 2. IP地址和主机名称配置3. 软件配置3.1 安装 epel-release3.2 卸载虚拟机自带的JDK3.3 克隆虚拟机3.4 修改克隆虚拟机的IP3.5 JDK安装3.6 Hadoop安装 4. Hadoop目录结构 1. Centos7.5硬件配置 1.1 创建虚拟机 1.2…

CMU 10423 Generative AI:lec18(大模型的分布式训练)

这个文档主要讲解了分布式训练&#xff08;Distributed Training&#xff09;&#xff0c;特别是如何在多GPU上训练大规模的语言模型。以下是主要内容的概述&#xff1a; 1. 问题背景 训练大规模语言模型的主要挑战是内存消耗。 训练过程中&#xff0c;内存消耗主要来源于两个…

关于Mac管理员root权限的一些问题总结

&#x1f389; 前言 最近在学习Vue CLI的时候&#xff0c;发现在Vscode里面想要修改文件或者保存文件都会显示“permission denied”&#xff0c;即权限不足。于是想了一些解决方法&#xff0c;记录在此。 &#x1f389; 检查当前用户权限 打开终端&#xff0c;输入以下指令…

yolov8/9/10/11模型在工地安全帽检测中的应用【代码+数据集+python环境+GUI系统】

yolov8/9/10/11模型在工地安全帽检测中的应用【代码数据集python环境GUI系统】 yolov8/9/10/11模型在工地安全帽检测中的应用【代码数据集python环境GUI系统】 背景意义 在建筑工地、矿山、工厂等工业生产环境中&#xff0c;安全帽是保护工人头部免受伤害的重要劳保工具。然而…

销售业绩飞跃,通过CRM系统激发销售团队潜力

要让销售人员使用CRM系统&#xff0c;首先需要理解他们抗拒的原因。常见的抗拒理由包括时间不足、系统复杂、缺乏培训以及对成效的怀疑。为了克服这些障碍&#xff0c;企业可以采取一系列措施&#xff0c;如提供全面培训、通过案例展示价值、强调实际收益、逐步引入系统、领导层…

C语言第15课—数据在内存中的存储

文章目录 1. 整数在内存中的存储2. 大小端字节序和字节序判断3. 整数存储练习3.1 练习13.2 练习23.3 练习33.4 练习43.5 练习5 4. 浮点数在内存中的存储4.1 浮点数存的过程4.2 浮点数取的过程 1. 整数在内存中的存储 整数的2进制表示有三种方法&#xff1a;原码、反码、补码有符…

CSS3--美若天仙!?

免责声明&#xff1a;本文仅做分享~ 目录 CSS引入方式 选择器 盒子尺寸和背景色 文字控制属性 单行文字 垂直居中 字体族 font复合属性 文本对齐方式 文本修饰线 color 文字颜色 ----- 复合选择器 伪类选择器 超链接伪类 CSS特性 继承性 层叠性 优先级 Emmet …

37 预处理器与预处理指令、宏定义(定义常量、数据类型、替换文本、嵌套与取消)、带参宏(细节处理、与函数的区别)

目录 1 预处理器 2 预处理指令 2.1 位置 2.2 格式 2.3 换行 2.4 结束符 2.5 位置限制 3 宏定义 3.1 语法格式 3.2 使用宏定义常量 3.3 使用宏定义数据类型 3.4 宏定义的替换文本 3.5 宏定义嵌套 3.6 取消宏定义 4 带参数的宏定义 4.1 语法格式 4.2 案例演示 …

ElasticSearch学习笔记(三)Ubuntu 2204 server elasticsearch集群配置

如果你只是学习elasticsearch的增、删、改、查等相关操作&#xff0c;那么在windows上安装一个ES就可以了。但是你如果想在你的生产环境中使用Elasticsearch提供的强大的功能&#xff0c;那么还是建议你使用Linux操作系统。 本文以在Ubuntu 2204 server中安装elasticsearch 8.…

go的一些知识点

一.package 1.新建项目 新建一个itying文件夹&#xff0c;在里面使用命令 就能生成一个go项目。生成一个go.mod 2.调用别的包的代码 按照下面的目录层级生成代码 //clac.go package calcfunc Add(x, y int) int {return x y } func Sub(x, y int) int {return x - y }…

【Web】复现n00bzCTF2024 web题解(全)

目录 File Sharing Portal 方法一&#xff1a; 方法二&#xff1a; Focus-on-yourSELF Passwordless File Sharing Portal 附件的Dockerfile给了这么一段 # Add the cron job to the crontab RUN mkdir /etc/cron.custom RUN echo "*/5 * * * * root rm -rf /app…

<<迷雾>> 第6章 加法机的诞生(1)--全加器 示例电路

全加器的符号 info::操作说明 鼠标单击开关切换开合状态 primary::在线交互操作链接 https://cc.xiaogd.net/?startCircuitLinkhttps://book.xiaogd.net/cyjsjdmw-examples/assets/circuit/cyjsjdmw-ch03-01-full-adder.txt 原图 全加器的逻辑电路实现 info::操作说明 鼠标单击…