C++ 继承:概念定义、对象的赋值转换、继承作用域及派生类的默认成员函数

news2025/6/17 19:11:28

目录

一. 继承的概念和定义

1.1 继承的概念

1.2 继承的定义格式

1.3 继承关系和访问限定符

二. 基类和派生类对象的之间的赋值转换

三. 继承体系中的作用域

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

4.1 构造函数 

4.2 拷贝构造函数

4.3 赋值运算符重载函数

4.4 析构函数

4.5 取地址运算符重载函数

附录:派生类6个默认成员函数的实现完整代码


一. 继承的概念和定义

1.1 继承的概念

继承是面向对象编程的三大特性之一(封装、继承、多态),继承使得程序可以在原有类的基础上进行扩展,在保留原有类功能的同时增加具有新的功能的新类新增的类称为派生类/子类,原有的类称为基类/父类。继承体现了面向对象程序设计的层次结构,是类层次设计的体现。

假设要定义一个学生类Student,学生类中要记录学生的相关信息(姓名、年龄、学号等),其中姓名和年龄为每个人都具备的基础信息,因此,可以单独定义一个Person类,其中包含姓名信息和年龄信息。Student类基础Person类,这样在Student类中,就包含了Person类的成员变量,同时可以调用Person类的成员函数。在这一继承体系中,Student为派生类/子类,Person为父类/基类。

图1.1 Student类和Person类的继承关系

1.2 继承的定义格式

定义继承体系,要包含三个关键要素:基类、派生类和继承关系。

图1.2 继承的定义方式

1.3 继承关系和访问限定符

通过类和对象的学习,可知访问限定符有三种:private(私有)、public(公有)、protected(保护)。同时,继承关系对应三种访问限定符,也可分为三种:private、public、protected。

图1.3 访问限定符和继承关系

基类中成员的访问限定符和继承关系,共同影响子类对基类成员的访问权限,表1.1为不同的基类成员访问方式和继承方式下,子类对基类成员的访问权限。

表1.1 子类对基类成员的访问权限
基类成员访问方式/继承方式publicprotectedprivate
pubilcpublicprotectedprivate
protectedprotectedprotectedprivate
private基类成员对子类不可见基类成员对子类不可见基类成员对子类不可见

根据表1.1,总结出以下规律:

  1. 基类中的private成员,对子类是不可见的,不可见指在子类中无法访问基类成员。
  2. 基类的protected(保护)成员不能在类外部被直接访问,但它的派生类可以访问基类的protected成员,如果希望基类的某成员不能被随意访问但可通过派生类访问,那么应该定义为protected成员。
  3. 派生类对基类成员的访问权限为:Min(基类成员访问方式,继承方式),其中:public > protected > private。

另有两点需要注意:

  1. class的默认继承方式为private继承,struct的默认继承方式为public继承,这里的class和struct是指定义派生类时使用的关键字,而不是基类。但是,一般要求继承方式要显示地写出,不可省略。
  2. 在绝大部分实际项目中,都是使用public继承,protected继承和private继承几乎用不到。

二. 基类和派生类对象的之间的赋值转换

关于基类和派生类对象之间的相互赋值,有以下几条关系:

  1. 派生类对象可以给基类对象赋值,但基类对象不能给派生类对象赋值。
  2. 派生类指针可以赋值给基类指针,基类指针通过强制类型转换可以赋值给派生类指针。但是基类指针给派生类指针赋值时,极易产生越界访问问题。
  3. 派生类对象可以赋值给基类引用,但基类对象的引用不能赋值给派生类对象。

派生类对象(指针)之所以可以赋值给基类对象(指针),是因为编译器对派生类对象进行了切割,将派生类和基类共有的部分从派生类中分离出来,赋值给基类。

图2.1 派生类给基类赋值时的切割关系示意图
class Person
{
public:
	std::string _name;
	int _age;
};

class Student: public Person
{
public:
	int _stuNum;  //学号
	int majorId;  //专业代号
};

int main()
{
	Person p1;
	Student s1;

	p1 = s1;   //派生类对象赋值给基类对象
	Person* pp1 = &s1;   //派生类指针赋值给基类指针
	Person& rp1 = s1;    //派生类赋值给基类引用

	Person p2;
	Student s2;

	//s2 = p2;   //基类对象不能赋值给派生类
	Student* pp2 = (Student*)&p1;  //基类指针通过强制类型转换可赋给派生类指针
	//pp2->_stuNum = 1;  //越界访问,程序崩溃
	//Student& rs2 = p1;  //基类不能赋给派生类引用

	return 0;
}

三. 继承体系中的作用域

关于继承体系中的类作用域,有下面几条语法规则:

  1. 基类和派生类成员都有自己单独的作用域,不可认为一个派生类中继承下来的基类成员位于这个派生类的作用域中。
  2. 如果基类和派生类中含有同名的成员变量,那么派生类成员会将基类成员覆盖,即默认先访问派生类成员。如果想访问基类中同名的成员变量,那么就应当指定作用域,语法格式为:基类名称::基类成员名。
  3. 基类和派生类中的函数,只要函数名相同,就存在覆盖问题,基类和派生类的成员函数无法构成重载,因为重载函数要求定义在同一作用域内。
  4. 在继承体系中,基类和派生类中应尽量避免出现同名成员。
class A
{
public:
	void func()
	{
		std::cout << "func()" << std::endl;
	}

	int _a = 1;
	int _a1 = 2;
};

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

	int _a = 10;
	int _b = 20;
};

int main()
{
	B b;

	std::cout << b._a << std::endl;  //10
	std::cout << b.A::_a << std::endl;  //通过指定作用域访问基类中同名成员 -- 1

	//b.func();  //默认访问派生类成员函数 -- 编译报错:函数参数不足
	b.func(10);  //func(int x)->10
	b.A::func(); //func()

	return 0;
}

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

继承中的派生类,与普通类一样,如果用户不显示定义,就会生成6个默认成员函数。

本文以Person和Student类为例,讲解如何通过自定义来实现派生类的六个默认成员函数。

class Person
{
private:
	std::string _name;  //姓名
	int _age;  //年龄
};

class Student: public Person
{
private:
	int _stuNum;  //学号
	int _majorId;  //专业代号
};

4.1 构造函数 

编译器自动生成的派生类构造函数:

  1. 对于派生类本身的成员变量,处理方式与普通类对象一致(内置类型成员变量不进行处理,自定义类型成员变量调用它的默认构造函数)。
  2. 调用基类的构造函数,初始化基类的成员。
  3. 构造顺序为:先基类对象 -> 再派生类对象

注意:如果要自己定义派生类构造函数,应当显示地调用基类的构造函数,最好在初始化列表中就进行调用。同时,不可以在派生类的构造函数中直接操作基类成员,基类成员变量只能在基类构造函数中进行处理。

//基类构造函数
Person(const char* name = "zhang", int age = 20)
	: _name(name)
	, _age(age)
{}

//派生类构造函数
Student(const char* name = "zhang", int age = 20, int stuNum = 1, int majorId = 0)
	: Person(name, age)  //调用基类的默认构造函数
	, _stuNum(stuNum)
	, _majorId(majorId)
{}

4.2 拷贝构造函数

编译的自动生成的派生类拷贝构造函数:

  1. 对于派生类本身的成员,与普通类一样,内置类型成员变量做浅拷贝,自定义类型成员变量调用它的拷贝构造函数。
  2. 调用基类拷贝构造函数创建基类对象。

如果要自己实现派生类拷贝构造函数,应当显示调用基类拷贝构造函数。

Student(const Student& s)
	: Person(s)
	, _stuNum(s._stuNum)
	, _majorId(s._majorId)
{ }

4.3 赋值运算符重载函数

编译器自动生成的赋值运算符重载函数与自动生成的拷贝构造函数类似,进行的工作为:

  1. 对于派生类本身的内置类型成员变量进行浅拷贝,自定义类型调用其赋值operator=函数。
  2. 调用基类的operator=函数。

与构造函数和拷贝构造函数一致,如果要自己定义operator=函数,要显示调用基类的operator=函数。这里要注意:调用基类的operator=函数时要指定基类作用域,否则会因为函数名相同而产生覆盖,引发无限递归调用。

Student& operator=(const Student& s)
{
	Person::operator=(s);   //显示调用基类的赋值运算符重载函数
	_stuNum = s._stuNum;
	_majorId = s._majorId;

	return *this;  //返回类对象本身
}

4.4 析构函数

编译器自动生成的派生类析构函数会先处理派生类成员变量,然后调用基类的析构函数。

如果自己实现派生类析构函数,注意不需要显示调用基类的析构函数,编译器会自动调用。这里是为了保证派生类对象先于基类对象被析构。

~Student()
{
	//清理资源...(无动态开辟内存就无需额外处理)
}

4.5 取地址运算符重载函数

一般而言,不需要用户自己实现取地址运算符重载函数,编译器自动生成的就足以满足要求。但即使要自己实现派生类的operator&函数,也不需要显示地再去调用基类的operator&函数。

//对于普通派生类对象的取地址运算符重载函数
Student* operator&()  
{
	return this;
}

//对于const派生类对象的取地址运算符重载函数
const Student* operator&() const  
{
	return this;
}

附录:派生类6个默认成员函数的实现完整代码

class Student: public Person
{
public:
	Student(const char* name = "zhang", int age = 20, int stuNum = 1, int majorId = 0)
		: Person(name, age)  //调用基类的默认构造函数
		, _stuNum(stuNum)
		, _majorId(majorId)
	{}

	Student(const Student& s)
		: Person(s)
		, _stuNum(s._stuNum)
		, _majorId(s._majorId)
	{ }

	Student& operator=(const Student& s)
	{
		Person::operator=(s);   //显示调用基类的赋值运算符重载函数
		_stuNum = s._stuNum;
		_majorId = s._majorId;

		return *this;  //返回类对象本身
	}

	~Student()
	{
		//清理资源...(无动态开辟内存就无需额外处理)
	}

	Student* operator&()  //对于普通派生类对象的取地址运算符重载函数
	{
		return this;
	}

	const Student* operator&() const  //对于const派生类对象的取地址运算符重载函数
	{
		return this;
	}

private:
	int _stuNum;  //学号
	int _majorId;  //专业代号
};

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

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

相关文章

linux服务器禁止ping命令,linux服务器禁ping如何解除

linux服务器禁止ping命令&#xff0c;linux服务器禁ping如何解除 我是艾西&#xff0c;在我们搭建网站或做某些程序时&#xff0c;不少人会问禁ping是什么意思&#xff0c;怎么操作的对于业务有哪些好处等&#xff0c;今天艾西一次给你们说清楚。 禁PING的意思是&#xff1a;不…

相对全面的四足机器人驱动规划MATLAB和Simulink实现方式(足端摆线规划,Hopf-CPG,Kimura-CPG)

许久没更新四足机器人相关的博客文章&#xff0c;由于去年一整年都在干各种各样的~活&#xff0c;终于把硕士毕业论文给写好&#xff0c;才有点时间更新自己的所学和感悟。步态规划和足端规划只是为了在运动学层面获取四足机器人各关节的期望角位移和速度信号&#xff0c;再由底…

力扣---两数相加(c语言版)

&#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言初阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:记录一个力扣写了好久的一个问题 金句分享: ✨在心里种花,人…

uniapp 实现可滑动顶部选项卡(菜单)

利用和实现可滑动顶部选项卡&#xff0c;实现的切换效果类似下图这种&#xff08;顶部tab下方swiper&#xff09;&#xff1a;&#xff08;代码具体效果只是实现可滑动顶部选项卡的功能&#xff0c;具体样式可自定义修改&#xff09; 一、分步骤展示 构建顶部tab选项按钮步骤…

00后卷王的自述,我难道真的很卷?

前言 前段时间去面试了一个公司&#xff0c;成功拿到了offer&#xff0c;薪资也从12k涨到了18k&#xff0c;对于工作都还没两年的我来说&#xff0c;还是比较满意的&#xff0c;毕竟一些工作3、4年的可能还没我高。 我可能就是大家说的卷王&#xff0c;感觉自己年轻&#xff…

[论文阅读笔记22]Identity-Quantity Harmonic Multi-Object Tracking

这是一篇优化匹配策略的文章, 通过密度估计的辅助, 估计一个边界框内有多少目标, 从而恢复遮挡的轨迹. 发表在IEEE TIP 2022. 论文地址: https://ieeexplore.ieee.org/abstract/document/9725267/ 0. Abstract 通常的MOT算法在匹配阶段时, 都是通过线性指派问题求解, 即会计算…

最近骗子有点多

就这几天&#xff0c;也就是这几天&#xff0c;我听到身边有两个人的家里人因为某某事情被骗。第一个是我旁边的同事&#xff0c;说是家里的老人被骗了好十几万&#xff0c;说是有一个高收益的旅行团&#xff0c;交钱了就免费旅游&#xff0c;旅游也是真去了&#xff0c;而且每…

一文弄懂访问者模式

关于设计模式&#xff0c;我们得结合生活中的案例来学习&#xff1b;最近我在网上也看了不少文章&#xff0c;今天想跟大家分享一下关于访问者模式的一些知识&#xff0c;先来看一个简单的案例吧。 相信大家都去过医院&#xff0c;看完病&#xff0c;医生都会给我们开一个处方…

数据结构:算法的时间复杂度和空间复杂度

Hello各位老铁们&#xff01;我们又见面了&#xff0c;大家最近有没有坚持学习和敲代码呢&#xff1f;在这里小编就要督促一下大家了&#xff0c;我们每一天都敲一两道题&#xff0c;这样子久而久之的坚持下去你就会发现你已经在不知不觉的过程中变成了一个大佬。敲代码使我们快…

unity蒙板测试例子——传送门

返回目录 Unity蒙板测试例子——传送门一、例子介绍 大家好&#xff0c;我是阿赵。 蒙板测试&#xff08;Stencil Test&#xff09;是渲染管线的合并输出环节里面的一种测试&#xff0c;和透明度测试或者深度测试一样&#xff0c;它实际上也是控制颜色值是否应该显示出来的一种…

natapp + nginx 实现内网穿透

环境是我本地的win10 第一步&#xff1a;下载nginx压缩包并解压(这个自行百度吧) 第二步&#xff1a; 修改nginx的配置文件&#xff1a; 在最下方添加&#xff1a; #testserver {listen 8081;#你要映射的端口server_name localhost; #使用本地IPlocation / {proxy_pass…

面试官:如何搭建Prometheus和Grafana对业务指标进行监控?

Prometheus和Grafana都是非常流行的开源监控工具&#xff0c;可以协同使用来实现对各种应用程序、系统、网络和服务器等的监视和分析。 下面对Prometheus和Grafana进行简要介绍&#xff1a; Prometheus Prometheus是一款开源、云原生的系统和服务监控工具&#xff0c;它采用p…

戴尔惠普联想笔记本性价比(戴尔和联想笔记本性价比)

联想。 联想做笔记本时间长&#xff0c;售后、质量都不错&#xff0c;追求稳定&#xff0c;性价比合理&#xff0c;长久使用考虑&#xff0c;不会有太大笔记本毛病。 联想1984就开始进入笔记本电脑行业&#xff0c;由中科院投资&#xff0c;后来收购了IBM之后&#xff0c;笔记…

第13届蓝桥杯省赛真题剖析-2022年4月17日Scratch编程初中级组

[导读]&#xff1a;超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成&#xff0c;后续会不定期解读蓝桥杯真题&#xff0c;这是Scratch蓝桥杯真题解析第122讲。 第13届蓝桥杯省赛举办了两次&#xff0c;这是2022年4月17日举行的第一次省赛&#xff0c;比赛仍然采取线上形…

Air32F103CBT6|CCT6|KEIL-uVsion5|本地编译|STClink|(6)、Air32F103编译下载

目录 一、环境搭建 准备工作 安装支持包 二、新建工程 添加外设库支持 测试代码 三、下载烧录 一、环境搭建 准备工作 安装MDK5&#xff0c;具体方法请百度&#xff0c;安装后需要激活才能编译大文件 下载安装AIR32F103的SDK&#xff1a;luatos-soc-air32f103: Air32f…

FL Studio2023中文版本水果编曲工具

编曲、剪辑、录音、混音&#xff0c;23余年的技术积淀和实力研发&#xff0c;FL Studio 已经从电音领域破圈&#xff0c;成功蜕变为全球瞩目的全能DAW&#xff0c;把电脑变成全功能音乐工作室&#xff0c;接下来我们会为您一一展示FL Studio 21也叫做水果音乐编曲软件&#xff…

Chat GPT太强,多种玩法曝光

1、模拟面试、考试&#xff0c;备考等&#xff0c;以面试为例&#xff0c;让它扮演一个角色 先让它扮演互联网行业的商业分析师 2、写各种发言稿、文章、报告&#xff0c;给它你的明确要求和目标&#xff0c;以发言稿为例&#xff0c;输入继续&#xff0c;可以持续优化 3、…

报名截至在即 | “泰迪杯”挑战赛最后一场赛前指导直播!

为推广我国高校数据挖掘实践教学&#xff0c;培养学生数据挖掘的应用和创新能力&#xff0c;增加校企交流合作和信息共享&#xff0c;提升我国高校的教学质量和企业的竞争能力&#xff0c;第十一届“泰迪杯”数据挖掘挑战赛&#xff08;以下简称挑战赛&#xff09;已于2023年3月…

白银实时行情操作中的一些错误及其解决办法(下)

小编根据大师&#xff0c;网络上的高手以及自己的经验整理出的一些交易中典型的错误&#xff0c;投资者可以参考参考&#xff0c;有则改之无则加勉~续上文…… 问题三&#xff1a;长线获利的交易不容易坚持同时陷入盘整或亏损的交易&#xff08;特别是大仓持有的品种&#xff…

银行数字化转型导师坚鹏:ChatGPT解密与银行应用案例

ChatGPT解密与银行应用案例 ——开启人类AI新纪元 打造数字化转型新利器 课程背景&#xff1a; 很多企业和员工存在以下问题&#xff1a; 不清楚ChatGPT对我们有什么影响&#xff1f; 不知道ChatGPT的发展现状及作用&#xff1f; 不知道ChatGPT的银行业应用案例&#xff1f;…