C++类和对象:构造函数和析构函数

news2024/11/25 4:44:56

目录

一. 类的六个默认成员函数

二. 构造函数

2.1 什么是构造函数

2.2 编译器自动生成的默认构造函数 

2.3 构造函数的特性总结

三. 析构函数

3.1 什么是析构函数

3.2 编译器自动生成的析构函数

3.3 析构函数的特性总结


一. 类的六个默认成员函数

对于任意一个C++类,如果用户不定义任何成员函数,那么编译器就会自动生成6个默认成员函数。当然,这6个成员函数也可以由用户自主定义,如果用户自主定义了,那么系统就不会自动生成。这6个默认成员函数包括:构造函数、析构函数、拷贝构造函数、赋值重载函数、对普通对象和const对象的取地址重载函数。

图1.1  C++类的6个默认成员函数

二. 构造函数

2.1 什么是构造函数

构造函数在定义类对象是由编译的自动调用,用于初始化类的成员变量。构造函数是一种十分特殊的成员函数,它没有返回值,它的函数名与类名相同。

任何一个类都不能没有默认构造函数,否则这个类就是非法的,在用这个类初始化对象时编译器会报错。默认构造函数,就是不需要参数就可以调用的构造函数,默认构造函数有三种可能的形式:

  1. 无参数
  2. 全缺省参数
  3. 由编译器自动生成的构造函数

演示代码2.1定义了一个日期类,并定义了一个全缺省的构造函数,这里由于用户自定义了构造函数,所有编译器不会生成默认构造函数。此外,还应当明确,构造函数是支持重载的,Date(int year = 1, int month = 1, int day = 1)可以被拆分为一个无参的构造函数Date()和一个不存在确实参数的构造函数Date(int year, int month, int day)。

演示代码2.1:

class Date  //日期类
{
public:
	Date(int year = 1, int month = 1, int day = 1)  //自定义默认构造函数
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	//定义类对象,调用默认构造函数
	Date d1;  //Date(1, 1, 1)
	Date d2(2023, 2, 26);  //Date(2023, 2, 26)

	d1.Print();  //1-1-1
	d2.Print();  //2023-2-26

	return 0;
}
图2.1 演示代码2.1的运行结果

2.2 编译器自动生成的默认构造函数 

当用户不自定义默认构造函数时,编译器就会自动生成默认构造函数,编译的自动生成的构造函数进行的工作为:

  1. 对于内置类型成员变量,不进行初始化
  2. 对于自定义类型成员变量,调用自定义类型成员变量的默认构造函数

其中,内置类型指int、char、double、float、指针、内置类型数组等C/C++自带的数据类型,自定义类型指使用struct/class定义的类或结构体数据类型。

为了验证编译器自动生成的构造函数所完成的功能,在演示代码2.2中定义了一个Date类和一个Time类,在Date类中不自定义构造函数,在Time类中定义默认构造函数将Time中的成员变量全部初始化为1,Date类中包括3个内置类型成员变量和1个Time类类型的成员变量。对程序进行调试,可见定义好的Date类对象d1中的内置类型成员变量都是随机值,而d1中的类对象t的成员变量都变变为了1。这就证实了上面对编译的自动生成的构造函数进行的工作的讲述的正确性。

演示代码2.2:

class Time
{
public:
	Time(int hour = 1, int minute = 1, int second = 1)  //自定义默认构造函数
	{
		_hour = hour;
		_minute = minute;
		_second = second;
	}

private:
	int _hour;
	int _minute;
	int _second;
};

class Date
{
private:
	int _year;
	int _month;
	int _day;
	Time t;
};

int main()
{
	Date d1;  //定义类对象,调用编译器自动生成的默认构造函数
	return 0;
}
图2.2  演示代码2.2的调试界面

我们可以认为默认构造函数不对内置类型成员变量做任何处理是C++的一个缺陷。为了弥补这一缺陷, C++11允许在定义类是显示的声明内置类型成员变量的初值。如演示代码2.3所示,Date类中的三个成员变量都被赋默认初值1。

演示代码2.3:

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year = 1;
	int _month = 1;
	int _day = 1;    //三个内置类型的成员变量的默认初值都为1(C++11)
};

int main()
{
	Date d;
	d.Print();  //1-1-1
	return 0;
}

2.3 构造函数的特性总结

  1. 构造函数是特殊的成员函数,函数名与类名相同,无返回值。
  2. 构造函数在对象实例化时由编译器自主调用,无需用户手动调用。
  3. 构造函数可以重载。
  4. 如果用户不显示定义构造函数,那么编译器就会生成默认的构造函数。

三. 析构函数

3.1 什么是析构函数

析构函数的功能与构造函数相反,在类对象生命周期结束时,由编译器自动调用,完成对类对象的资源清理工作,析构函数的函数名为:~类名。

析构函数可以由用户自身定义,也可以由编译器自主生成。当拥护显示的定义了析构函数时,编译器不自动生成析构函数,但当用户不显示的定义析构函数时,编译器会自动生成默认的析构函数。

析构函数没有参数、没有返回值。

演示代码3.1定义了一个简易的栈类Stack,其中定义了三个成员变量:int* _a -- 存储栈数据的数组、int _top -- 栈顶位置下标、int _capacity -- 栈容量,还定义了四个成员函数:Stack -- 栈初始化函数(构造函数)、Push -- 压栈函数、Print -- 栈中数据打印函数、~Stack -- 栈销毁函数(析构函数)。在~Stack函数中释放了动态开辟的内存。

在主函数中,我们创建了一个栈类对象S,程序的运行结果表明,构造函数和析构函数先后被调用。

演示代码3.1:

class Stack
{
public:
	Stack(int capacity = 3)  //构造函数,初始化栈,类实例化时自动调用
	{
		cout << "Stack()" << endl;

		_a = (int*)malloc(capacity * sizeof(int));  //为栈数组动态开辟内存空间
		if (nullptr == _a)
		{
			cout << "malloc fail" << endl;
			exit(-1);
		}

		_capacity = capacity;
		_top = 0;
	}

	void Push(int x)  //压栈函数,x为压入数据
	{
		if (_top == _capacity)  //检查栈是否已满
		{
			int* tmp = (int*)realloc(_a, (_capacity * 2) * sizeof(int));  //开辟新的内存空间
			if (tmp == nullptr)
			{
				cout << "realloc fail" << endl;
			}

			_a = tmp;
			_capacity *= 2;
		}

		_a[_top++] = x;
	}

	void Print()  //栈中数据打印函数
	{
		for (int i = 0; i < _top; ++i)
		{
			cout << _a[i] << " ";
		}
		cout << endl;
	}

	~Stack()  //析构函数,在对象生命周期结束时自动调用
	{
		cout << "~Stack()" << endl;

		free(_a);  //释放栈空间
		_a = nullptr;
		_top = _capacity = 0;
	}

private:
	int* _a;  //存储栈数据的数组
	int _top;  //栈顶下标
	int _capacity;  //栈容量
};

int main()
{
	Stack S;  //类实例化,调用默认构造函数

	S.Push(1);
	S.Push(2);
	S.Push(3);
	S.Push(4);
	S.Push(5);  //将1、2、3、4、5压入栈中
	S.Print();  //打印栈中数据

	return 0;  //对象生命周期结束,调用析构函数
}
图3.1  演示代码3.1的运行结果

3.2 编译器自动生成的析构函数

如果用户不显示定义析构函数,则编译器会生成默认的析构函数,编译器自动生成的析构函数所执行的操作与编译器自动生成的构造函数类似:

  1. 对于内置类型成员变量,不做任何处理。
  2. 对于自定义类型成员变量,去掉它的析构函数。

演示代码3.2定义了一个名为TwoStack的类,其中包含两个Stack类的自定义类型成员变量S1和S2,在主函数中对类进行实例化,创建对象ts,在ts创建和销毁时都调用Stack的默认构造函数和析构函数。

演示代码3.2:

class TwoStack
{
private:
	Stack S1;
	Stack S2;
};

int main()
{
	TwoStack st;  //类实例化,调用默认构造函数
	return 0;  //对象生命周期结束,调用析构函数
}
图3.2  演示代码3.2运行结果

问题:什么时候可以使用编译器自动生成的析构函数,什么时候必须显示定义析构函数? 

  • 当类中的成员变量仅在栈区占用内存空间时,类对象的生命周期结束是成员变量所占用的空间自动被释放,不需要析构函数执行额外的操作,如日期类Date,就不需要自定义析构函数。
  • 当类的成员变量执行堆区开辟的某一块空间时,如通过malloc动态开辟了内存空间,就需要显示的定义析构函数来进行资源清理,如Stack类就不能使用编译器自动生成的析构函数。

3.3 析构函数的特性总结

  1. 析构函数的函数名是在类名前面加~。
  2. 析构函数无参数、无返回值。
  3. 析构函数在类对象生命周期结束时由编译器自主调用。
  4. 一个类有且只有一个析构函数,要么由用户显示定义,要么由编译器自动生成。
  5. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如
    Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

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

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

相关文章

零基础如何入门网络安全(黑客)

我经常会看到这一类的问题&#xff1a; 学习XXX知识没效果&#xff1b;学习XXX技能没方向&#xff1b;学习XXX没办法入门&#xff1b; 给大家一个忠告&#xff0c;如果你完全没有基础的话&#xff0c;前期最好不要盲目去找资料学习&#xff0c;因为大部分人把资料收集好之后&a…

三天吃透Java虚拟机面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

趣味数学题存疑待证1

原文出自&#xff1a;球面上随机 N 个点在同一个半球上的概率 要求任意N个点&#xff0c;全在同一个半球上的概率&#xff0c;我们需要构造使得分母为有限的样本集合&#xff0c;分子则为有N个点在同一半球的情况集 首先对任意N个点取其对称点使得可划分点为2N&#xff0c;在…

【数据库】第七章 数据库设计

第七章数据库设计 数据库设计概述 数据库设计的基本步骤 需求分析概念结构设计逻辑结构设计物理结构设计数据库实施数据库运行和维护 需求分析 收集需求&#xff0c;理解需求 收集各个角色的需求 概念数据库设计 建立概念模型 &#xff0c;E-R图/IDEF1x图 消除冲突&…

【JDK8新特性之日期时间API-案例实操】

一.JDK8新特性之日期时间API-案例实操 之前我们学习了Stream流、Lambda表达式以及方法引用等相关的内容&#xff0c;如果想学习的同学可以看一下之前的文章&#xff0c;接下来我们一起学习一下关于JDK8中新日期时间API的使用。 二.JDK中原始日期时间存在的问题 设计不合理&…

VC++随鼠标移动显示坐标和坐标映射模式学习

VC6&#xff0c;新建一个单文档工程&#xff1b; 把这两个加到视类头文件&#xff1b; CPoint m_ptOut; //用来保存鼠标坐标 CString strPosition; //用来保存输出的内容 视类CPP的构造函数中初始化&#xff0c; m_ptOut.x 0; m_ptOut.y 0; 用类向导为视类…

Boosting Crowd Counting via Multifaceted Attention之人群密度估计实践

这周闲来无事&#xff0c;看到一篇前不久刚发表的文章&#xff0c;是做密集人群密度估计的&#xff0c;这块我之前虽然也做过&#xff0c;但是主要是基于检测的方式实现的&#xff0c;这里提出来的方法还是比较有意思的&#xff0c;就拿来实践一下。论文在这里&#xff0c;感兴…

chatGPT模型原理

文章目录简介BertGPT 初代GPT-2GPT-3chatGPT开源ChatGPT简介 openai 的 GPT 大模型的发展历程。 Bert 2018年&#xff0c;自然语言处理 NLP 领域也步入了 LLM 时代&#xff0c;谷歌出品的 Bert 模型横空出世&#xff0c;碾压了以往的所有模型&#xff0c;直接在各种NLP的建模…

Java中的反射使用

1、获取Class对象的三种方式 1、对象调用Object类的getClass()方法&#xff08;对象.getClass()&#xff09; 2、调用类的class属性&#xff08;类名.class&#xff09; 3、调用Class类的静态方法&#xff08;Class.forName(“包名.类名”)&#xff09;常用 Student类 package…

Xcode Developer Document 开发者文档

总目录 iOS开发笔记目录 从一无所知到入门 文章目录IntroDeveloper Documentation 打开方式菜单栏点击 &#xff5c; 快捷键方式另一种打开方式Intro 2016年我在学校学Java的时候&#xff0c;要查某个Java类/方法的用法还得自己手动下载一种.chm格式的开发文档文件&#xff0c…

python爬虫常见错误

python爬虫常见错误前言python常见错误1. AttributeError: WebDriver object has no attribute find_element_by_id1. 问题描述2. 解决办法2. selenium&#xff1a;DeprecationWarning: executable_path has been deprecated, please pass in1. 问题描述2. 解决办法3. 下载了包…

4、算法MATLAB---认识矩阵

认识矩阵1、矩阵定义和基本运算1.1 赋值运算符&#xff1a;1.2 等号运算符&#xff1a;1.3 空矩阵1.4 一行一列矩阵1.5 行矩阵&#xff08;元素用空格或逗号分隔&#xff09;1.6 列矩阵&#xff08;分号表示换行&#xff09;1.7 m行n列的矩阵&#xff1a;行值用逗号间隔&#x…

SPI总线设备驱动模型

SPI总线设备驱动模型 文章目录SPI总线设备驱动模型参考资料&#xff1a;一、平台总线设备驱动模型二、 数据结构2.1 SPI控制器数据结构2.2 SPI设备数据结构2.3 SPI设备驱动三、 SPI驱动框架3.1 SPI控制器驱动程序3.2 SPI设备驱动程序致谢参考资料&#xff1a; 内核头文件&…

角角的Qt自学日记:Qt的安装

目录 2. 打开下载器&#xff0c;输入账号和密码&#xff0c;然后单击下一步&#xff1a; 3. 分别单击2个单选框&#xff0c;其它不用管&#xff0c;直接单击下一步&#xff1a; 4. 先设置一下安装目录&#xff0c;因为现在Qt基本都好几个g&#xff0c;建议找个内存够的盘。然…

尝试用程序计算Π(3.141592653......)

文章目录1. π\piπ2. 用微积分来计算π\piπ2.1 原理2.2 代码2.3 结果2.4 分析1. π\piπ π\piπ的重要性或者地位不用多说&#xff0c;有时候还是很好奇&#xff0c;精确地π\piπ值是怎么计算出来的。研究π\piπ的精确计算应该是很多数学家计算机科学家努力的方向&#xf…

【老卫搬砖】034期:HarmonyOS 3.1 Beta 1初体验,我在本地模拟器里面刷短视频

今天啊打开这个DevEco Studio的话&#xff0c;已经提示有3.1Beta1版本的一个更新啊。然后看一下它的一些特性。本文也演示了如何在本地模拟器里面运行HarmonyOS版短视频。 主要特性 新特性包括&#xff1a; Added support for Windows 11 64-bit and macOS 13.x OSs, as well…

vue中render函数的作用和参数(vue2中render函数用法)

render 函数是 Vue2.x 新增的一个函数、主要用来提升节点的性能&#xff0c;它是基于 JavaScript 计算。使用 Render 函数将 Template 里面的节点解析成虚拟的 Dom 。Vue 推荐在绝大多数情况下使用模板来创建 HTML。然而在一些场景中&#xff0c;需要 JavaScript 的完全编程能力…

RK3568平台开发系列讲解(驱动基础篇)GIC v3中断控制器

🚀返回专栏总目录 文章目录 一、什么是GIC二、GIC v3中断类型三、GIC v3基本结构3.1、Distributor3.2、CPU interface简介3.3、Redistributor简介3.4、ITS(Interrupt translation service)4、中断状态和处理流程沉淀、分享、成长,让自己和他人都能有所收获!😄 📢ARM多核…

在线文档技术-编辑器篇

这是在线文档技术的第二篇文章&#xff0c;本文将对目前市面上所有的主流编辑器和在线文档进行一次深入的剖析和研究&#xff0c;从而使大家对在线文档技术有更深入的了解&#xff0c;也让更多人能够参与其开发与设计中来。 注意&#xff1a;出于对主流文档产品的尊重&#xf…

【Linux环境配置】7. Linux部署code-server

安装 code-server 两种方法&#xff0c;一种是在线安装&#xff0c;另一种是本地安装。因为主机访问github可能会报443错误&#xff0c;因此这里我推荐使用本地安装方法&#xff01; 本地安装方法 进入github&#xff0c;搜索code-server找到项目地址&#xff1a;https://gi…