C++面向对象程序设计 - 派生类的构造函数和析构函数

news2024/11/24 17:38:04

        构造函数的主要作用对数据成员初始化,基类的构造函数是不能被继承的,在声明派生类时,派生类并没有把类的构造函数继承下来。因此,对继承过来的基类成员初始化的工作也要由派生类的构造函数完成;所以在派生类中不仅要考虑自身增加的数据成员的初始化,还要考虑基类的数据成员的初始化。

一、派生类的构造函数

        解决方法则是,在执行派生类的构造函数时,调用基类的构造函数。

        其一般形式为

派生类构造函数名 ( 总参数表列 ) : 基类构造函数名 ( 参数表列 ) 

{

        派生类中新增数据成员初始化语句

}

        这里通过人与学生类示例来演示,将Person类数据成员设置为保护成员,这样方便直接在派生类中调用。代码如下:

#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
	protected:
		string name;	// 姓名
		int age;		//年龄
		char gender;	//性别
	public:
		Person(){
			name = "anonym";
			age = 0;
			gender = 0;
		}
		Person(string name, int age, char gender): name(name), age(age), gender(gender){}
};
// 派生类 - 学生
class Student: public Person{
	private:
		string school;
	public:
		Student(string name, int age, char gender, string school): Person(name, age, gender){
			this->school = school;
		}
		void display(){
			cout <<"name:" <<name <<endl;
			cout <<"age:" <<age <<endl;
			cout <<"gender:" <<(gender=='1'?"male":"female") <<endl;
			cout <<"school:" <<school <<endl;
		}
};
int main(){
	Student s("Tom", 18, '1', "middle school");
	s.display();
	return 0;
}

        运行结果如下:

在建立一个对象时,执行构造函数的顺序:

  1. 派生类构造函数先调用基类构造函数;
  2. 再执行派生类构造函数本身(即派生类构造函数的函数体)。

二、有子对象的派生类的构造函数

        在类对象中,数据成员都是标准类型(如int,char)或系统提供的类型(如string),但实际上,类的数据成员中还可以包含类对象。类对象中内嵌对象,即对象中的对象,称为子对象。

        这里将学生对应的班长,先以Person类表示,在Student类中定义子对象并在派生类构造函数中初始化。代码如下:

#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
	protected:
		string name;	// 姓名
		int age;		//年龄
		char gender;	//性别
	public:
		Person(){
			name = "anonym";
			age = 0;
			gender = 0;
		}
		Person(string name, int age, char gender): name(name), age(age), gender(gender){}
		void display(){
			cout <<"name:" <<name <<endl;
			cout <<"age:" <<age <<endl;
			cout <<"gender:" <<(gender=='1'?"male":"female") <<endl;
		}
};
// 派生类 - 学生
class Student: public Person{
	private:
		Person monitor;		// 定义子对象(班长)
		string school;
	public:
		// 构造函数,并初始化基类和子对象
		Student(string name, int age, char gender, string school, string m_name, int m_age, char m_gender): 
			Person(name, age, gender), school(school), monitor(m_name, m_age, m_gender){}
		void display(){
			cout <<"name:" <<name <<endl;
			cout <<"age:" <<age <<endl;
			cout <<"gender:" <<(gender=='1'?"male":"female") <<endl;
			cout <<"school:" <<school <<endl;
			
			cout <<endl <<"monitor:" <<endl;
			monitor.display();
		}
};
int main(){
	Student s("Tom", 18, '1', "middle school", "John", 19, '1');
	s.display();
	return 0;
}

        运行结果如下:

        派生类构造函数的任务包括三部分:

  1. 对基类数据成员初始化;
  2. 对子对象数据成员初始化;
  3. 对派生类数据成员初始化。

        定义派生类构造函数的一般形式:

派生类构造函数名 (总参数表列) : 基类构造函数名 (参数表列), 子对象名 (参数表列)

{

        派生类中新增数据成员初始化语句

}

        执行派生类构造函数的顺序:

  1. 调用基类构造函数,对基类数据成员初始化;
  2. 调用子对象构造函数,对子对象数据成员初始化;
  3. 再执行派生类构造函数本身,对派生类数据成员初始化。

三、多层派生时的构造函数

        一个类不仅可以派生出一个派生类,派生类还可以继续派生,形成派生的层次结构。

        这里在Person类和Student类基础上再定义一个Teacher类,其顺序是Person -> Student -> Teacher。示例代码如下:

#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
	protected:
		string name;	// 姓名
		int age;		//年龄
		char gender;	//性别
	public:
		Person(){
			name = "anonym";
			age = 0;
			gender = 0;
		}
		Person(string name, int age, char gender): name(name), age(age), gender(gender){}
};
// 派生类 - 学生
class Student: public Person{
	private:
		string school;
	public:
		// 构造函数,并初始化基类
		Student(string name, int age, char gender, string school): 
			Person(name, age, gender), school(school){}
		string show_school(){
			return school;
		}
		void display(){
			cout <<"name:" <<name <<endl;
			cout <<"age:" <<age <<endl;
			cout <<"gender:" <<(gender=='1'?"male":"female") <<endl;
			cout <<"school:" <<school <<endl;
			cout <<endl;
		}
};
class Teacher: public Student{
	private:
		string course;			//课程
	public:
		// 构造函数,并初始化基类
		Teacher(string name, int age, char gender, string school, string course): 
			Student(name, age, gender, school), course(course){}
		void display(){
			cout <<"name:" <<name <<endl;
			cout <<"age:" <<age <<endl;
			cout <<"gender:" <<(gender=='1'?"male":"female") <<endl;
			cout <<"school:" <<show_school() <<endl;
			cout <<"course:" <<course <<endl;
			cout <<endl;
		}
};
int main(){
	Student s("Tom", 18, '1', "middle school");		//定义学生类对象
	Teacher t("Lily", 30, '2', "middle school", "Mathematics");	//定义教师类对象
	
	s.display();	// 显示学生信息
	t.display();	//显示教师信息
	return 0;
}

        运行结果如下:

        初始化顺序:

  1. 先初始化基类(Person)的数据成员;
  2. 再初始化派生类(Student)的数据成员;
  3. 最后再初始化派生类(Teacher)的数据成员。

四、派生类的析构函数

        析构函数的作用是在对象撤销之前,进行必要的清理工作。当对象被删除时,系统会自动调用析构函数。

        在派生类中析构函数不能被继承,也需要通过派生类的析构函数去调用基类的析构函数。在派生类中可以根据需要定义析构函数,用来对派生类中所增加的成员进行清理工作,基类的清理工作扔然由基类的析构函数完成。在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理。

        析构函数的调用顺序刚好与构造函数相反,析构函数是先调用派生类的析构函数,对派生类的成员进行清理,然后调用子对象的析构函数对子对象进行清理,最后调用基类的析构函数对基类进行清理。

        这里将子对象中代码稍作修改,来演示析构函数执行顺序,示例代码如下:

#include <iostream>
#include <string>
using namespace std;
// 基类 - 人类
class Person{
	protected:
		string name;	// 姓名
		int age;		//年龄
		char gender;	//性别
	public:
		Person(){
			name = "anonym";
			age = 0;
			gender = 0;
		}
		Person(string name, int age, char gender): name(name), age(age), gender(gender){}
		// 基类的析构函数
		~Person(){
			cout <<name <<", Person constructor" <<endl;
		}
};
// 派生类 - 学生
class Student: public Person{
	private:
		Person monitor;		// 定义子对象(班长)
		string school;
	public:
		// 构造函数,并初始化基类和子对象
		Student(string name, int age, char gender, string school, string m_name, int m_age, char m_gender): 
			Person(name, age, gender), school(school), monitor(m_name, m_age, m_gender){}
		// 派生类的析构函数
		~Student(){
			cout <<name <<", Student constructor" <<endl;
		}
};
int main(){
	Student s("Tom", 18, '1', "middle school", "John", 19, '1');
	return 0;
}

        运行结果如下:

        由图可见,基类(Person类)是最后执行的,子对象(John对象)是在基类之前执行,而最先执行的则是派生类(Student类)。

        这个过程是确保了资源在正确的顺序中被释放。

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

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

相关文章

OSPF的LSA与特殊区域

Area区域概念 *一个区域维护一张LSDB&#xff0c;路由器详细的链路信息只在这个区域内传播 不是每一台路由器都需要了解所有外部目的地的详细信息 *OSPF网络的层次化设计 通过区域ID标识 骨干&#xff08; Backbone &#xff09;区域&#xff0c;必须是area 0(骨干区域…

JVM(Jvm如何管理空间?对象如何存储、管理?)

Jvm如何管理空间&#xff08;Java运行时数据区域与分配空间的方式&#xff09; ⭐运行时数据区域 程序计数器 程序计数器&#xff08;PC&#xff09;&#xff0c;是一块较小的内存空。它可以看作是当前线程所执行的字节码的行号指示器。Java虚拟机的多线程是通过时间片轮转调…

milvus对象存储和消息中间件的工厂设计模式分析

milvus对象存储和消息中间件的工厂设计模式分析 需求 根据参数设置创建mq和storage mq有kafka,pulsar storage有local,minio,remote 配置文件 根据配置文件选择初始化mq和存储: mq:type: pulsarcommon:storageType: minio对于这种类型一个是mq&#xff0c;一个是存储&…

抓住四月小尾巴,拿个offer~

首先声明一下~本人是个双非二本大三在校生。 从三月份就开始了苦哈哈的找实习之旅&#xff0c;快三月中旬才敢投大厂&#xff0c;为什么嘞&#xff1f;因为学校要求必须参加完期末考试才能出去实习&#xff08;差不多七月初&#xff09;&#xff0c;因为这个好多公司一听就不安…

算法模版自用(杂)

文章目录 算法库函数next_permutation(start,end) prev_permutation(start,end) (全排列函数)nth_element &#xff08;求第k小值&#xff09;next(it,num),prev(it,num)min_element(begin(),end()),max_element(begiin(),end()) (取最小值最大值) _int128的输入输出STLlist 数…

serdes 同轴电缆和双绞线接法

1、同轴电缆 Coaxial Cable 2、双绞线STP&#xff08;Shielded Twisted Pair&#xff09; 比如我们用的车载camera一般就只需要接一路即可&#xff0c;RIN接camera&#xff0c; RIN-通过电容接地。

Android 使用 GeckoView 并实现 js 交互、权限交互

参考文档&#xff1a; geckoview版本 引入文档&#xff08;有坑 下面会给出正确引入方式&#xff09; 官方示例代码1 官方示例代码2 参考了两位大神的博客和demo&#xff1a; GeckoView js交互实现 geckoview-jsdemo 引入方式&#xff1a; maven {url "https://maven.…

MySQL中的死锁预防和解决

MySQL中的死锁预防和解决 死锁是数据库管理系统中常见的问题&#xff0c;特别是在高并发的应用场景下。MySQL数据库中的死锁会导致事务处理速度减慢&#xff0c;甚至完全停止&#xff0c;因此理解并预防死锁至关重要。本文将详细介绍如何预防MySQL中的死锁&#xff0c;包括常用…

【算法基础实验】图论-深度优先搜索和深度优先路径

深度优先(DFS) 理论基础 深度优先搜索&#xff08;DFS, Depth-First Search&#xff09;是图和树的遍历算法中的一种&#xff0c;它从一个节点开始&#xff0c;沿着树的边走到尽可能深的分支&#xff0c;直到节点没有子节点为止&#xff0c;然后回溯继续搜索下一个分支。DFS …

网络安全实训Day17and18

写在前面 第17和18天都讲的sql注入&#xff0c;故合并 ​​​​​​ 网络空间安全实训-渗透测试 Web渗透 定义 针对Web站点的渗透攻击&#xff0c;以获取网站控制权限为目的 Web渗透的特点 Web技术学习门槛低&#xff0c;更容易实现 Web的普及性决定了Web渗透更容易找到目…

JavaEE 初阶篇-深入了解 I/O 高级流(缓冲流、交换流、数据流和序列化流)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 缓冲流概述 1.1 缓冲流的工作原理 1.2 使用缓冲流的步骤 1.3 字节缓冲流于字符缓冲流的区别 1.4 字节缓冲流的实例 1.5 字符缓冲流的实例 2.0 转换流概述 2.1 字符…

MySQL函数之单行函数

1.前言 我们在使用 SQL 语言的时候&#xff0c;不是直接和这门语言打交道&#xff0c;而是通过它使用不同的数据库软件&#xff0c;即DBMS。DBMS 之间的差异性很大&#xff0c;远大于同一个语言不同版本之间的差异。实际上&#xff0c;只有很少的函数是被 DBMS 同时支持的。比…

MySQL基础知识——MySQL索引

深入浅出索引 索引的意义 索引的意义&#xff1a;在大量数据中&#xff0c;加速访问少量特定数据&#xff1b; 使用索引的前提条件&#xff1a; 1&#xff09;索引块数量小于数据块数量&#xff1b; 2&#xff09;索引键有序&#xff0c;故可以使用二分查找等高效的查找方式&…

go语言并发实战——日志收集系统(十) 重构tailfile模块实现同时监控多个日志文件

前言 在上一篇文章中&#xff0c;我们实现了通过etcd来同时指定多个不同的有关分区与日志文件的路径&#xff0c;但是锁着一次读取配置的增多&#xff0c;不可避免的出现了一个问题&#xff1a;我们如何来监控多个日志文件&#xff0c;这样原来的tailFile模块相对于当下场景就…

前端到全栈进阶之“前端框架”

从前端入门到全栈-系列介绍 你会学到什么&#xff1f; 可能学不到什么东西&#xff0c;该系列是作者本人工作和学习积累&#xff0c;用于复习 系列介绍 现在的 Web 前端已经离不开 Node.js&#xff0c;我们广泛使用的 Babel、Webpack、工程化都是基于 Node 的&#xff0c;各…

【Linux】驱动_2_字符驱动

1. Linux设备分类 字符设备: 指应用程序按字节/字符来读写数据的设备。通常为传真、虚拟终端和串口调制解调器、键盘之类设备提供流通信服务&#xff0c;通常不支持随机存取数据。字符设备在实现时大多不使用缓存器。系统直接从设备读/写每一个字符。块设备: 通常支持随机存取…

【程序分享1】LAMMPS + OVITO + 晶体缺陷识别 + 点缺陷 + 分子动力学模拟

分享2个分子动力学模拟相关的程序。 1. 一种识别体心立方晶体缺陷的新方法。 2. 无后处理的分子动力学模拟中的并行点缺陷识别: lammps的计算和转储方式 。 感谢论文的原作者&#xff01; 第1个程序 关键词&#xff1a; 1. Atomistic simulations, 2. Molecular dynamics…

让客服工作开挂的8个客服办公高效率神器

做客服工作&#xff0c;经常需要写文案&#xff0c;做图片做视频&#xff0c;还要能快捷回复客户&#xff0c;都需要有靠谱的客服办公软件支持&#xff0c;本文介绍了8个高效神器&#xff0c;希望能帮到做客服的亲 前言 做客服工作&#xff0c;在回答客户咨询的同时&#xff0…

2024.4.28 机器学习周报

目录 引言 Abstract 文献阅读 1、题目 2、引言 3、创新点 4、总体流程 5、网络结构 5.1、损失函数 5.2、Confidence Maps 5.3、Part Affinity Fields(PAFs) 5.4、多人的PAFs 6、实验 7、结论 深度学习 yolov8实现目标检测和人体姿态估计 Yolov8网络结构 yaml…

【亲测可用】配置镜像源

文章目录 配置镜像源1. 手动添加镜像源2. 永久配置&#xff08;推荐&#xff09;方法1&#xff1a;方法2 &#xff1a; 小结 配置镜像源 配置镜像源会让资源下载的更快一些 我实验了一下&#xff0c;都成功了的方法&#xff0c;推荐给你们 1.手动添加 2.永久配置 前提是你的…