C++进阶--继承

概念

继承,允许一个类(称为子类或派生类)从另一个类(称为父类或基类)继承属性和方法
继承的主要目的是实现代码的重用和构建类之间的层次关系。通过继承,子类可以获得父类的特性,包括数据成员和成员函数,而无需重新编写相同的代码。

定义

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

class Teacher :public Person
{
protected:
	int _TId;
};

int main()
{
	Student s;
	
	Teacher t;
	s.Print();
	t.Print();

	return 0;
}

格式

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

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

在这里插入图片描述

可以看出,基类的private成员在派生类无论以何种方式继承都是不可见的这里的不可见是虽然基类的成员仍然被继承到了,但在语法上限制了对private成员的访问,也就说如果要初始化派生类的继承成员,是没被办法初始化的
基类的protected成员以任何方式继承都是可以的,但是不同的继承方式展示出不同的权限;
使用关键字class默认继承方式是private,使用关键字struct默认继承方式是public,但还是写出继承方式比较好;
在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承。

基类和派生类的赋值转换

1.派生类对象可以赋值给基类的对象/基类的指针/基类的引用。由于子类可能会比父类多一些成员什么的,当派生类赋值给基类时,会切割基类没有的数据成员和成员函数,这种做法被称为“切片”;
2.基类对象不能赋值给派生类对象
3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用

class Person
{
protected:
	string _name="PersonName"; // 姓名
	string _sex; // 性别
	int _age=10; // 年龄
};
class Student : public Person
{
public:
	void Print()
	{
		cout<<"-name:" << _name << endl;
		cout<<"age:" << _age << endl;
		cout << "Personname:" << Person::_name << endl;
	}
protected:
	int _No; // 学号
	string _name="StudentName";
};
int main()
{
	Student s;
	
	//派生类赋值给基类
	Person p1=s;
	Person* p2 = &s;
	Person& p3 = s;

	//基类赋值给派生类
	//s = p1;//基类不能赋值给派生类

	Student* ps1 = (Student*)p2;//可以通过强制类型转换赋值给派生类
	


	return 0;
}

在这里插入图片描述
在这里插入图片描述

继承中的作用域

1.在继承体系中基类和派生类都有各自独立的作用域
2.在继承体系中最好不要定义同名的成员
3. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏
也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
4. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏
在这里插入图片描述

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

在这里插入图片描述

派生类的默认成员函数

1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认构造函数,那么可以在派生类中的构造函数初始化列表中进行显示调用;
2.派生类的拷贝构造必须调用基类的拷贝构造完成基类的拷贝初始化
3.派生类的赋值必须调用基类的operator=完成基类的复制
4.派生类的析构函数会在被调用完成后再调用基类的析构函数完成清理。这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

class Person
{
public:
	Person(string name = "hama")
		: _name(name)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
		: _name(p._name)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person operator=(const Person& p)" << endl;
		if (this != &p)
			_name = p._name;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name; // 姓名
};
class Student : public Person
{
public:
	Student(string 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;
	}
	Student& operator = (const Student& s)
	{
		cout << "Student& operator= (const Student& s)" << endl;
		if (this != &s)
		{
			Person::operator =(s);
			_num = s._num;
		}
		return *this;
	}
	~Student()
	{
		cout << "~Student()" << endl;
	}
protected:
	int _num; //学号
};

int main()
{
	Student s1("傻风",12);//构造
	Student s2(s1);//拷贝构造
	Student s3("傻怕", 18);//赋值
	s1 = s3;

	return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

继承与友元

友元函数无法继承,也就是说,基类的友元不能访问子类私有和保护成员。

class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
void main()
{
Person p;
Student s;
Display(p, s);
}

在这里插入图片描述
这个好理解,父亲的朋友怎么会是孩子的朋友呢?除非让父亲的朋友也跟孩子做朋友,当然这样就不叫继承了;
在这里插入图片描述
!](https://img-blog.csdnimg.cn/direct/15e5065f11a04d4bbd198f2eeb6c692d.png)

继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员,它是独立于基类和派生类之外作用域的;只是恰好在基类中当成员。静态成员伴随着整个运行阶段;

class Person
{
public:
	Person() { ++_count; }
protected:
	string _name; // 姓名
public:
	static int _count; // 统计人的个数。
};
int Person::_count = 0;//初始化

class Student : public Person
{
protected:
	int _stuNum; // 学号
};
class Graduate : public Student
{
protected:
	string _seminarCourse; // 研究科目
};
int main()
{
	Student s1;
	Student s2;
	Student s3;
	Graduate s4;
	cout << " 人数 :" << Person::_count << endl;
	Student::_count = 0;
	cout << " 人数 :" << Person::_count << endl;
}

在这里插入图片描述

菱形继承

在上面最近代码中,一个子类只有一个父类时这种继承成为单继承;
多继承:一个子类有两个或以上的父类时称这个继承为多继承
菱形继承:如下
在这里插入图片描述
菱形继承的问题:

class A
{
public:
	int _a;
};
// class B : public A
class B :virtual  public A
{
public:
	int _b;
};
// class C : public A
class C :virtual  public A
{
public:
	int _c;
};
class D : public B, public C
{
public:
	int _d;
};

在上面代码中,类D同时继承了类C和类B,而类C和类B又都继承了类A,这就构成了菱形继承的情况了;
菱形继承的主要问题是二义性。由于类D继承了两个父类,并且这两个父类都有相同的成员a,编译器无法确定具体调用B类的a还是C类的a给D类继承,从而导致二义性问题;

int main()
{
	D d;
	//虽然菱形继承可以通过显示调用来解决二义性问题,当仍然存在数据冗余
	d.B::_a = 1;
	d.C::_a = 2;

	d._b = 3;
	d._c = 4;
	d._d = 5;

	return 0;
}

在这里插入图片描述
为了解决菱形继承的二义性,C++提供了虚拟继承的机制;就是在类B和类C继承类A时加上关键字virtual,可以告诉编译器在构建类D的对象时只保留一份A类的实例
在这里插入图片描述

在实际应用中,我们很少用到多继承,也就没有菱形继承的问题;

组合和继承

继承和组合是面向对象编程中两种常见的代码复用机制。
继承:继承是一种通过创建新类基于已有类的属性和方法来实现代码复用的机制,子类可以继承父类的所有公有和受保护的成员,并且可以在此基础上添加新的特性或修改现有的行为。通过继承可以实现类之间的“is-a”关系,即子类是父类的一种特殊类型。
组合:组合是通过将一个类的对象作为另一个类的成员来实现代码复用的机制。像我们前面的逆向迭代器,stack和queue都是用到了组合;通过组合,一个类可以拥有其他类的对象,并且可以通过调用这些对象的方法来实现自己的功能。组合主要用于表示“has-a”关系,即一个类包含另一个类的对象。

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

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

相关文章

海外短剧系统国际短剧源码h5多语言版app挂载tiktok油管ins

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目 前言 一、海外短剧系统是什么&#xff1f; 二、海外短剧系统功能与运营方式介绍 1.系统功能 2.短剧APP运营方式 总结 前言 本文简单介绍海外短剧系统的功能&#xff…

【已解决】Ubuntu64位无法运行32位ELF文件的问题

问题起因 因为在做一道逆向题&#xff0c;发现题目给的文件是32位elf文件&#xff0c;所以想在Linux下执行一下&#xff0c;然后发现会报错。 于是查了一下资料&#xff0c;发现报错的原因是64位的Ubuntu无法直接运行32位的程序&#xff0c;需要下载兼容32位的库。 解决方法…

JUC并发编程10——ThreadLocal

目录 1. ThreadLocal是什么&#xff1f; 2. ThreadLocal怎么用&#xff1f; 3. ThreadLocal源码分析 3.1set方法 3.2get()方法 3.3remove()方法 4.为什么key使用弱引用&#xff1f; 5.ThreadLocalMap 和 HashMap 区别 6.ThreadLocal变量不具有传递性 7.InheritableTh…

Android 跳转应用设置/热点界面或等常用操作

Android 跳转应用设置/热点界面或等常用操作 https://www.jianshu.com/p/ba7164126690 android学习进阶——Setting https://blog.csdn.net/csdn_wanziooo/article/details/81980984 Android 7.1 以太网反射 EthernetManager 配置 DHCP、静态 IP https://codeleading.com/art…

防火墙综合拓扑(NAT、双机热备)

实验需求 拓扑 实验注意点&#xff1a; 先配置双机热备&#xff0c;再来配置安全策略和NAT两台双机热备的防火墙的接口号必须一致如果其中一台防火墙有过配置&#xff0c;最好清空或重启&#xff0c;不然配置会同步失败两台防火墙同步完成后&#xff0c;可以直接在主状态防火墙…

MyBatis 的注解实现方法

MyBatis 的注解实现方法 MyBatis 的注解实现方法引入依赖添加配置创建表创建实体类创建mapper接口InsertDeleteSelectResults和ResultMap通过配置文件解决 UpdateOptions MyBatis 的注解实现方法 引入依赖 在springBoot项目中下载了EditStarters插件的,可以直接在配置文件处右…

JVM学习

1.Java虚拟机内部有哪些线程共享&#xff0c;那些线程隔离 程序计数器&#xff1a; 通过改变这个计数器的值来选取下一条需要执行的字节码命令 Java虚拟机栈&#xff1a; 栈&#xff0c;每个方法被执行时&#xff0c;Java虚拟机都会同步的创建一个栈帧用于存储局部变量表&…

Linux:进度条的创建

目录 使用工具的简单介绍&#xff1a; \r &#xff1a; fflush &#xff1a; 倒计时的创建&#xff1a; 倒计时的工作原理&#xff1a; 进度条的创建&#xff1a; 不同场景下、打印任意长度的进度条&#xff1a; main .c procbor.c 测试效果&#xff1a; 使用工具…

STM32学习笔记(四) —— 位段别名区的使用

STM32F103RCT6有两个位段区 (SRAM 最低1M空间和片内外设存储区最低1M空间)&#xff0c; 这两个区域都有各自的别名区&#xff0c;在别名区中每个字会映射到位段区的一个位&#xff0c;所以在别名区修改一个字相当于修改位段区中对应的一个位 映射公式( 别名区中的字与位段区中的…

jenkins部署(docker)

docker部署&#xff0c;避免安装tomcat 1.拉镜像 docker pull jenkins/jenkins2.宿主机创建文件夹 mkdir -p /lzp/jenkins_home chmod 777 /lzp/jenkins_home/3.启动容器 docker run -d -p 49001:8080 -p 49000:50000 --privilegedtrue -v /lzp/jenkins_home:/var/jenkins_…

Excel得到JSON串

很多时候业务都需要做一种从Excel读取或者导入数据的功能&#xff0c;这在cs程序比较简单&#xff0c;在BS程序上如果封装不好的话那么写起来还是很费劲的&#xff0c;这次封装Excel读取操作。 先看使用 对&#xff0c;你没有看错&#xff0c;就是这么简单。 封装 基础设计…

2023年葡萄酒行业分析报告(电商数据查询):消费市场疲软,但国产品牌的替代效应逐步明显

近几年&#xff0c;受国内经济增速放缓的影响&#xff0c;现阶段国内葡萄酒的消费需求仍显不足。同时&#xff0c;当前国内酒类市场正处于存量竞争阶段&#xff0c;市场竞争十分激烈&#xff0c;其他酒类也在一定程度上挤占了葡萄酒的市场份额&#xff0c;这也导致国内葡萄酒消…

04:容器技术概述|镜像与容器|docker配置管理

容器技术概述&#xff5c;镜像与容器&#xff5c;docker配置管理 什么是容器优点缺点 docker与容器跳板机yum源添加docker软件在node节点验证软件包开启路由转发 镜像管理&容器管理如何获取镜像镜像备份与恢复运行容器查看镜像的启动信息镜像管理命令容器管理命令容器内部署…

十一、常用API——练习

常用API——练习 练习1 键盘录入&#xff1a;练习2 算法水题&#xff1a;练习3 算法水题&#xff1a;练习4 算法水题&#xff1a;练习5 算法水题&#xff1a; 练习1 键盘录入&#xff1a; 键盘录入一些1~100之间的整数&#xff0c;并添加到集合中。 直到集合中所有数据和超过2…

SolidWorks曲面功能介绍

在SolidWorks中提供了功能丰富的曲面功能&#xff0c;那为什么我们需要使用曲面功能&#xff1f;曲面功能一般是在处理一些复杂外形的时候来使用&#xff0c;这些形状需要通过曲线的变化来控制&#xff0c;从而得到满意的外形&#xff0c;一般来说这样的外形是很难通过实体建模…

集成学习之Boosting方法系列_XGboost

文章目录 【文章系列】【前言】【算法简介】【正文】&#xff08;一&#xff09;XGBoost前身&#xff1a;梯度提升树&#xff08;二&#xff09;XGBoost的特点&#xff08;三&#xff09;XGBoost实际操作1. 前期准备&#xff08;1&#xff09;数据格式&#xff08;2&#xff09…

(2)(2.10) LTM telemetry

文章目录 前言 1 协议概述 2 配置 3 带FPV视频发射器的使用示例 4 使用TCM3105的FSK调制解调器示例 前言 轻量级 TeleMetry 协议 (LTM) 是一种单向通信协议&#xff08;从飞行器下行的数据链路&#xff09;&#xff0c;可让你以低带宽/低波特率&#xff08;通常为 2400 波…

Mamba系列日积月累(一):状态空间模型SSM的离散化过程推导

文章目录 1. 背景基础知识1.1 什么是状态空间模型&#xff08;State Space Model&#xff0c;SSM&#xff09;&#xff1f;1.2 什么是离散化&#xff08;Discretization&#xff09;&#xff1f;1.3 为什么需要离散化&#xff1f; 2. SSM离散化过程推导2.1 为什么在离散化过程中…

Windows断开映射磁盘提示“此网络连接不存在”,并且该磁盘直在资源管理器中

1、打开注册表编辑器 快捷键winR 打开“运行”&#xff0c; 输入 regedit 2、 删除下列注册表中和无法移除的磁盘相关的选项 \HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\MountPoints2\ 3、打开“任务管理器”&#xff0c;重新启动“Windows资源…

vue3源码(三)computed

1.功能 接受一个 getter 函数&#xff0c;并根据 getter 的返回值返回一个不可变的响应式 ref 对象。 默认不执行&#xff0c;在取值时执行&#xff0c;具有缓存功能&#xff0c;数据不变多次取值只触发一次取值计算。 import {reactive,effect,computed,} from "/node_…