【C++继承】

目录

  • 一、继承的概念及定义
    • 1.1继承的概念
    • 1.2继承的定义
      • 1.2.1定义格式
      • 1.2.2继承方式与访问限定符的组合
  • 二、基类和派生类对象赋值转换
  • 三、继承中的作用域
  • 四、派生类的默认成员函数
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、复杂的菱形继承及菱形虚拟继承
  • 八、虚拟继承的原理

一、继承的概念及定义

1.1继承的概念

继承是面向对象的三大特性之一。从字面上再结合程序员的特性就可以大概了解继承的主要思想,
通俗的讲就是继承过来的拥有被继承的属性。它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类,称派生类(或子类),被继承的类称基类(或父类)。如:

#include<iostream>
using namespace std;

class person
{
public:
	char* _name;//名字
	char* _Id;  //身份证
	size_t _age;//年龄
	int _sex;   //1表示男  0表示女
};
class student :public person
{
public:
	int _level;//年级
	string _major;//专业
};
class teacher :public person
{
public:
	int _job_title;//职称
};
int main()
{
	return 0;
}

简单来说呢就是person是teacher与student相同的部分所抽出来的。这样的好处就是方便了代码的编写。程序员的最大特点就是会考虑代码的复用,特别是资深的程序员极其讨厌写重复的代码。
在这里插入图片描述

1.2继承的定义

1.2.1定义格式

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

1.2.2继承方式与访问限定符的组合

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

二、基类和派生类对象赋值转换

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。如:

class person
{
public:
	char* _name;//名字
	char* _Id;  //身份证
	size_t _age;//年龄
	int _sex;   //1表示男  0表示女
};

class student :public person
{
public:
	int _level;//年级
	string _major;//专业
};

int main()
{
	person p;
	student s;

	p = s;
	return 0;
}

在这里插入图片描述
注意这里并没有发生隐式的类型转化。如何来证明呢?如下:
在这里插入图片描述

三、继承中的作用域

1.子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用基类::基类成员 显示访问)
2.需要注意的是如果成员函数的隐藏,只需要函数名相同就构成隐藏。
3.注意在实际中在继承体系里面最好不要定义同名的成员。
如:
在这里插入图片描述
在这里插入图片描述
两个func不会构成重载,是隐藏/重定义关系。要记住重载是发生在同一作用域下的。

四、派生类的默认成员函数

1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认
的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

class person
{
public:
	person(const char* name="左方婷")
		:_name(name)
	{
	}
protected:
	string _name;
};

class student : public person
{
public:
	student(const char* name = "zuofangting", int num = 20)
		:_num(20),
		person()
	{}
protected:
	int _num;
};

int main()
{
	return 0;
}
  1. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
student(const student& s)
	:_num(s._num),
	person(s)
{
	cout << "student的拷贝构造函数调用" << endl;
}

这里的person(s)就是上面讲的切片现象。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

student& operator=(const student& s)
{
	if (this != &s)
	{
		_num = s._num;
		person::operator=(s);
	}
	return *this;
}

这里必须要指定是person的作用域下的=重载,不然会无线调用自己的operator=。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能
保证派生类对象先清理派生类成员再清理基类成员的顺序。不需要我们自己调用派生类的析构。
在这里插入图片描述
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
7. 因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同(这个后面会讲
解)。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加
virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。如:
在这里插入图片描述

五、继承与友元

一句话概括就是你与父亲是好朋友并不能保证你与我也是好朋友。

class student;
class person
{

public:
	friend void Print(const person& p, const student& s);

	person(const char* name="左方婷")
		:_name(name)
	{
		cout << "person的构造调用" << endl;
	}

	person(const person& p)
		:_name(p._name)
	{
		cout << "person的拷贝构造调用" << endl;
	}

	person& operator=(const person& p)
	{
		_name = p._name;
	}

	~person()
	{
		cout << "person的析构函数调用" << endl;
	}
protected:
	string _name;
};



class student : public person
{

public:
	friend void Print(const person& p, const student& s);

	student(const char* name = "zuofangting", int num = 20)
		:_num(20),
		person(name)
	{
		cout << "student的构造调用" << endl;
	}

	student(const student& s)
		:_num(s._num),
		person(s)
	{
		cout << "student的拷贝构造函数调用" << endl;
	}

	student& operator=(const student& s)
	{
		if (this != &s)
		{
			_num = s._num;
			person::operator=(s);
		}
		return *this;
	}

	~student()
	{
		//person::~person();
		cout << "student的析构函数调用" << endl;
	}
protected:
	int _num;
};

void Print(const person& p, const student& s)
{
	cout << p._name << endl;
	cout << s._num << endl;

}

int main()
{
	student s1("wuzhilong",12);
	//student s2(s1);

	return 0;
}

六、继承与静态成员

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

class person
{

public:


	person(const char* name="左方婷")
		:_name(name)
	{
		++_count;
		cout << "person的构造调用" << endl;
	}


	~person()
	{
		cout << "person的析构函数调用" << endl;
	}

	static int _count;
protected:
	string _name;
};

int person::_count = 0;

class student : public person
{

public:


	student(const char* name = "zuofangting", int num = 20)
		:_num(20),
		person(name)
	{
		cout << "student的构造调用" << endl;
	}

	~student()
	{
		//person::~person();
		cout << "student的析构函数调用" << endl;
	}
protected:
	int _num;
};



int main()
{
	student s1("wuzhilong",12);
	//student s2(s1);
	cout << person::_count << endl;
	return 0;
}

七、复杂的菱形继承及菱形虚拟继承

在这里插入图片描述
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
在Assistant的对象中Person成员会有两份。

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; // 主修课程
};
void Test()
{
	// 这样会有二义性无法明确知道访问的是哪一个
	Assistant a;
	a._name = "peter";
	// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	a.Student::_name = "xxx";
	a.Teacher::_name = "yyy";
}

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和
Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地
方去使用。

class Person
{
public:
	string _name; // 姓名
};
class Student : virtual public Person
{
protected:
	int _num; //学号
};
class Teacher : virtual public Person
{
protected:
	int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
	string _majorCourse; // 主修课程
};
void Test()
{

	Assistant a;
	a._name = "peter";

}
int main()
{
	return 0;
}

八、虚拟继承的原理

在这里插入图片描述
下图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下
面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指
向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量
可以找到下面的A。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C++进阶之路】多态篇

文章目录 前言一、概念1.分类2.实现条件①重写虚函数1.1总结三重1.2 final与override ②父类的指针或者引用2.1普通调用VS多态调用 3.抽象类3.1. 纯虚函数3.2. 接口继承和实现继承 二、原理及使用1.虚函数表 —— 虚表2.默认成员函数2.1构造函数2.2析构函数 3. 多继承3.1普通的…

用Ubuntu交叉编译Linux内核源码并部署到树莓派4B上

参考文章 1. 配置交叉编译环境 之前在ubuntu上配置过了&#xff0c;直接跳过 2.获取Linux内核源码 Linux内核源码链接 到链接里面选择自己合适版本的内核源码下载下来&#xff0c;然后传到ubuntu中进行解压 3.Linux内核源码的配置 参考文章 厂家配linux内核源码&#xff…

深入理解Gradle构建系统的工作原理

&#x1f337;&#x1f341; 博主猫头虎 带您 Go to New World.✨&#x1f341; &#x1f984; 博客首页——猫头虎的博客&#x1f390; &#x1f433;《面试题大全专栏》 文章图文并茂&#x1f995;生动形象&#x1f996;简单易学&#xff01;欢迎大家来踩踩~&#x1f33a; &a…

使用贝叶斯滤波器通过运动模型和嘈杂的墙壁传感器定位机器人研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【C++】多态的实现及其底层原理

个人主页&#xff1a;&#x1f35d;在肯德基吃麻辣烫 我的gitee&#xff1a;gitee仓库 分享一句喜欢的话&#xff1a;热烈的火焰&#xff0c;冰封在最沉默的火山深处。 文章目录 前言一、什么是多态&#xff1f;二、多态的构成条件2.1什么是虚函数&#xff1f;2.2虚函数的重写2…

ICASSP 2023 | Cough Detection Using Millimeter-Wave FMCW Radar

原文链接&#xff1a;https://mp.weixin.qq.com/s?__bizMzg4MjgxMjgyMg&mid2247486540&idx1&sn6ebd9f58e9f08a369904f9c48e12d136&chksmcf51beb5f82637a3c65cf6fa53e8aa136021e35f63a58fdd7154fc486a285ecde8b8521fa499#rd ICASSP 2023 | Cough Detection Usi…

Qt中postevent造成内存泄漏问题的通用解决方案

在Qt中由QCoreApplication统一管理Qt事件的收发和销毁,其中sendEvent为阻塞式发送,用于单线程的事件发送;postevent为非阻塞式发送,构造事件的线程和接受事件的线程可以为两个线程。 最近在做一个个人项目ShaderLab 需要绘制OpenGL实时渲染的图像,由于OpenGL渲染基本都放…

论文笔记——Influence Maximization in Undirected Networks

Influence Maximization in Undirected Networks ContributionMotivationPreliminariesNotations Main resultsReduction to Balanced Optimal InstancesProving Theorem 3.1 for Balanced Optimal Instances Contribution 好久没发paper笔记了&#xff0c;这篇比较偏理论&…

python+django+mysql项目实践一(环境准备)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 创建Pycharm项目 安装Django 在pycharm文件—设置进行安装 新建Django项目 注意项目创建目录 项目默认目录文件说明: __init__.py asgi.py 【异步接受网络…

深度学习实践——模型推理优化练习

系列实验 深度学习实践——卷积神经网络实践&#xff1a;裂缝识别 深度学习实践——循环神经网络实践 深度学习实践——模型部署优化实践 深度学习实践——模型推理优化练习 深度学习实践——模型推理优化练习 模型推理优化练习架构设计练习知识蒸馏练习模型剪枝练习参数量化练…

如何高效实现文件传输:小文件采用零拷贝、大文件采用异步io+直接io

一般会如何实现文件传输&#xff1f; 服务器提供文件传输功能&#xff0c;需要将磁盘上的文件读取出来&#xff0c;通过网络协议发送到客户端。如果需要你自己编码实现这个文件传输功能&#xff0c;你会怎么实现呢&#xff1f; 通常&#xff0c;你会选择最直接的方法&#xf…

QT基于TCP协议实现数据传输以及波形绘制——安卓APP及Windows程序双版本

文章代码有非常非常之详细的解析&#xff01;&#xff01;&#xff01;诸位可放心食用 这个玩意我做了两个&#xff0c;一个是安卓app&#xff0c;一个是Windows程序。代码并非全部都是由我从无到有实现&#xff0c;只是实现了我想要的功能。多亏了巨人的肩膀&#xff0c;开源…

小研究 - 一种复杂微服务系统异常行为分析与定位算法(一)

针对极端学生化偏差&#xff08;&#xff25;&#xff58;&#xff54;&#xff52;&#xff45;&#xff4d;&#xff45; &#xff33;&#xff54;&#xff55;&#xff44;&#xff45;&#xff4e;&#xff54;&#xff49;&#xff5a;&#xff45;&#xff44; &#…

Java中的生产者/消费者模型

一、什么是生产者/消费者模型 生产者-消费者模型&#xff08;Producer-Consumer problem&#xff09;是一个非常经典的多线程并发协作的模型。 比如某个模块负责生产数据&#xff0c;而另一个模块负责处理数据。产生数据的模块就形象地被称为生产者&#xff1b;而处理数据的模…

后端一次返回大量数据,前端做分页处理

问题描述&#xff1a;后端接口返回大量数据&#xff0c;没有做分页处理&#xff0c;不支持传参pageNum&#xff0c;pageSize 本文为转载文章&#xff0c;原文章&#xff1a;后端一次返回大量数据&#xff0c;前端做分页处理 1.template中 分页 <el-paginationsize-chang…

39.手机导航

手机导航 html部分 <div class"phone"><div class"content"><img class"active" src"./static/20180529205331_yhGyf.jpeg" alt"" srcset""><img src"./static/20190214214253_hsjqw…

DHCP部署与安全详解

文章目录 一、DHCP是什么&#xff1f;二、DHCP相关概念三、DHCP优点四、DHCP原理1. 客户机发送DHCP Discovery广播包&#xff08;发现谁是DHCP服务器&#xff09;2. 服务器响应DHCP Offer广播包3. 客户机发送DHCP Request广播包4. 服务器发送DHCP ACK广播包 五、DHCP续约六、部…

ElasticSearch基本使用--ElasticSearch文章一

文章目录 官网学习必要性elasticsearch/kibana安装版本数据结构说明7.x版本说明ElasticSearch kibana工具测试后续我们会一起分析 官网 https://www.elastic.co/cn/ 学习必要性 1、在当前软件行业中&#xff0c;搜索是一个软件系统或平台的基本功能&#xff0c; 学习Elastic…

使用 OpenCV 进行图像模糊度检测(拉普拉斯方差方法)

写在前面 工作中遇到&#xff0c;简单整理人脸识别中&#xff0c;对于模糊程度较高的图像数据&#xff0c;识别率低&#xff0c;错误率高。虽然使用 AdaFace 模型&#xff0c;对低质量人脸表现尤为突出。但是还是需要对 模糊程度高的图像进行丢弃处理当前通过阈值分类&#xff…

Java开发中的分层开发和整洁架构

分层开发(横向拆分) 分层开发的概念: maven多模块开发项目管理.可以利用这种管理功能,实现一个项目的多层次模块开发–分层开发. 比如,当前项目HelloController依赖HelloService 这样做目的: 复杂开发过程.解耦(不调整依赖关系,无法解耦).分层开发(横向拆分)和纵向拆分的区别…