【C++】构造函数与析构函数

写在前面

构造函数与析构函数都是属于类的默认成员函数!
默认成员函数是程序猿不显示声明定义,编译器会中生成。

构造函数和析构函数的知识需要建立在有初步类与对象的基础之上的,关于类与对象不才在前面笔记中有详细的介绍:点我跳转


文章目录

  • 写在前面
  • 一、构造函数的特性
    • 1.1、函数名与类名相同。
    • 1.2、 无返回值。
    • 1.3、 对象实例化时编译器自动调用对应的构造函数。
    • 1.4、构造函数可以重载。
    • 1.5、如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。
    • 1.6、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。
  • 二、析构函数
    • 2.1、析构函数名是在类名前加上字符 ~
    • 2.2、无参数无返回值类型
    • 2.3、一个类只能有一个析构函数。
    • 2.4、对象生命周期结束时,C++编译系统系统自动调用析构函数。
    • 2.5、编译器生成的默认析构函数,对自定类型成员调用它的析构函数
    • 2.6、如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数


一、构造函数的特性

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象

其特征如下:

1.1、函数名与类名相同。

1.2、 无返回值。

1.3、 对象实例化时编译器自动调用对应的构造函数。

在这里插入图片描述

class stack {
public:
	stack() {//构造函数
		cout << "this is stack()" << endl;
	}
	
	void Init(int defintCapacity) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

int main() {
	stack s1;

	return 0;
}

程序运行结果:
在这里插入图片描述

  • 在上述代码中,不才创建了一个默认构造函数stack,在构造函数中,我们只让其打印字符串this is stack(),之后,我们在s1对象中,并没有显示的调用构造函数,但是字符串就被打印出来了,这就说明的对象实例化时编译器自动调用对应的构造函数

这时候,我们就可以把stack的初始化函数设置放入构造函数中,每当我们创建一个对象时,通过构造函数自动初始化数据。如下:

class stack {
public:
	stack(int defintCapacity = 4) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

int main() {
	stack s1;

	return 0;
}

运行结果:
在这里插入图片描述

  • 这时候,我们就不用每次都显示的初始化数据了,而且也不怕忘记初始化。

1.4、构造函数可以重载。

构造函数也是函数,是函数就可以重载
在这里插入图片描述

class stack {
public:
	stack(int defintCapacity = 4) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;
	}

	stack(int* arr, int defintCapacity) {
		if (nullptr == arr) {
			perror("malloc申请空间失败");
			return;
		}
		_arr = arr;
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

int main() {
	int* arr = (int*)calloc(2, sizeof(int));

	stack s1(arr, 2);

	return 0;
}

程序运行结果:
在这里插入图片描述
和函数重载一样的逻辑,编译器会根据符号名去调用对应的构造函数。
需要注意,调用默认构造函数不需要加括号,因为加上括号后,编译器会认为是函数

举个栗子:
stack s1:这时s1代表的是调用stack的默认构造函数的对象
stack s1():这时s1就被当做,返回值是stack类且没有形参的函数。
有参调用就和普通函数一样,只不过是对象+参数列表stack s1(arr, 2)


1.5、如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦用户显式定义编译器将不再生成。

默认构造函数在C++中有特殊定义,在C++标准中,默认构造函数不会对内置类型进行处理,自定义类型会调用它的默认构造函数。但是现在有些编译器会对内置类型进行初始化,但这是该编译器自己的行为,C++标准中是不进行处理的。

内置类型/基本类型:语言本身定义的基础类型(如intchar、指针、double等)
自定义类型:使用classstruct等定义的类型

在这里插入图片描述

class stack {
public:
	stack(int defintCapacity = 4) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;
	}

	stack(int* arr, int defintCapacity) {
		if (nullptr == arr) {
			perror("malloc申请空间失败");
			return;
		}
		_arr = arr;
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

class Date
{
public:

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

private:
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1;

	return 0;
}

程序运行结果:(在vs2022环境下)
在这里插入图片描述

在默认构造函数中,并不会对定义类型进行任何操作,貌似不能证明默认构造函数的存在,但是我们把Date类设置为,下程序时:

class stack {
public:
	stack(int defintCapacity = 4) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;
	}

	stack(int* arr, int defintCapacity) {
		if (nullptr == arr) {
			perror("malloc申请空间失败");
			return;
		}
		_arr = arr;
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

class Date
{
public:

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

private:
	//内置类型
	int _year;
	int _month;
	int _day;
	//自定义类型
	stack _st;
};

int main() {
	Date d1;

	return 0;
}

运行结果:(在VS2013编译器中)
在这里插入图片描述

  • 在vs2013中,我们可以清晰看出内置类型不会进行处理的,而自定义类型会调用其默认构造函数

但是我们在VS2022中尝试一下

在这里插入图片描述

  • 我们发现在vs2022编译环境下,有自定义类型情况中,内置类型会被初始化为0,在上例中,我们也发现,在没有自定义类型情况中,内置类型是不会处理的

所以,不才推荐在C++中类中,我们默认内置类型是未被处理的,自定义类型是会调用其默认构造函数的,这样不会出现程序运行错误。

C++11后,对成员变量做了一个补丁,可以在声明成员变量时给定一个缺省值。

在这里插入图片描述
这里不才以内置类型为例,

class Date
{
public:
	Date(){}
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	//内置类型 
	//这里不是初始化,而是声明
	//这里给的是默认缺省值,给编译器生成默认构造函数时使用
	int _year = 1;
	int _month = 1;
	int _day = 1;

};

int main() {
	Date d1;
	d1.Print();
	return 0;
}

程序运行结果:

在这里插入图片描述
如果我们调用默认构造函数,那么内置类型的值就是程序猿给定的缺省值。如果我们调用不是默认构造函数,那么使用的就是自定义构造函数的值,如下图。
在这里插入图片描述

什么情况下可以直接使用默认构造函数:

  • 内置类型成员都有缺省值,且初始化符合要求
  • 全部都是自定义类型成员,且这些类型都定义了默认构造函数。

1.6、无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。

虽然在语法中,无参的构造函数和全缺省的构造函数形参了函数重载,编译不会有错,但是在对象初始化时,无参调用存在歧义。

在这里插入图片描述

class Date
{
public:
	Date() {}

	Date(int year = 2035, int month = 1, int day = 1) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

private:
	int _year = 1;
	int _month = 1;
	int _day = 1;

};

int main() {
	Date d1;
	d1.Print();
	return 0;
}

程序运行结果:

在这里插入图片描述

  • 无参构造函数和全缺省的构造函数都是不需要传参调用的,所以在函数调用时,就会报错对重载函数的调用不明确

无参构造函数全缺省构造函数、我们没写编译器默认生成的构造函数,只要不传参就可以调用的,都可以认为是默认构造函数,而默认构造函数只能存在一个!

(未完…)

二、析构函数

析构函数:与构造函数功能相反析构函数不是完成对对象本身的销毁局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数完成对象中资源的清理工作

析构函数是特殊的成员函数,其特征如下:

2.1、析构函数名是在类名前加上字符 ~

2.2、无参数无返回值类型

2.3、一个类只能有一个析构函数。

若未显式定义,系统会自动生成默认的析构函数。注意:析构函数没有形参所以不能重载

2.4、对象生命周期结束时,C++编译系统系统自动调用析构函数。

在这里插入图片描述

class Date
{
public:
	Date() {
		cout << "Date()" << endl;
	}

	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}

	~Date() {
		cout << "~Date()" << endl;
	}

private:
	int _year = 1;
	int _month = 1;
	int _day = 1;

};

int main() {
	Date d1;
	{//创建了代码块用于验证:对象生命周期结束时,C++编译系统系统是否会自动调用析构函数
		Date d2;
		d2.Print();
		printf("\n");

		cout << &d2 << endl;
	}
	printf("\n");

	d1.Print();
	return 0;
}

程序运行结果:

在这里插入图片描述


2.5、编译器生成的默认析构函数,对自定类型成员调用它的析构函数

在这里插入图片描述

class stack {
public:
	stack(int defintCapacity = 4) {
		_arr = (int*)calloc(defintCapacity, sizeof(int));
		if (nullptr == _arr)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = defintCapacity;
		_size = 0;

		cout << "stack()" << endl;
	}

	stack(int* arr, int defintCapacity) {
		if (nullptr == arr) {
			perror("malloc申请空间失败");
			return;
		}
		_arr = arr;
		_capacity = defintCapacity;
		_size = 0;
	}

	void push(int x) {
		//....扩容等
		_arr[_size++] = x;
	}

	~stack() {
		free(_arr);
		_arr = nullptr;
		cout << "~stack()" << endl;
	}
private:
	int* _arr;
	int _size;
	int _capacity;
};

class Date
{
public:
	Date() {
		cout << "Date()" << endl;
	}

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

private:
	//内置类型 给定缺省值
	int _year = 1;
	int _month = 1;
	int _day = 1;
	//自定义类型
	stack _st;
};

int main() {
	Date d1;
	d1.Print();
	return 0;
}

程序运行结果:

在这里插入图片描述

  • main方法中创建了Date对象d1,而d1中包含4个成员变量,其中_year, _month,_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可。
  • 但是_ststack类的对象,所以在d1销毁时,要将其内部包含的stack类的_st对象销毁,所以要调用stack类的析构函数
  • main函数中不能直接调用stack类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date生成一个默认的析构函数,目的是在其内部调用stack类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁

2.6、如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数

析构函数的使用:

  1. 一般情况下,有动态申请资源 ,就需要显示写析构函数释放资源
  2. 没有动态申请资源,不需要写析构函数
  3. 需要释放资源的类型都是自定义类型,在该类中就不需要写析构函数。因为默认生成的析构函数遇到自定义类型会自动调用自定义类型的析构函数
  4. 特殊场景特殊使用

以上就是本章所有内容。若有勘误请私信不才。万分感激💖💖 如果对大家有用的话,就请多多为我点赞收藏吧~~~💖💖
请添加图片描述

ps:表情包来自网络,侵删🌹

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

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

相关文章

WPF区域导航+导航参数使用+路由守卫+导航日志

背景&#xff1a;使用ContentControl控件实现区域导航是有Mvvm框架的WPF都能使用的&#xff0c;不限于Prism 主要是将ContenControl控件的Content内容在ViewModel中切换成不同的用户控件 下面是MainViewModel&#xff1a; private object body;public object Body {get { retu…

Unity中 Xlua使用整理(一)

1.安装: 从GitHub上下载Xlua源码 Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com) 下载Xlua压缩包&#xff0c;并解压将Aseet文件夹中的Xlua和Plugins文件夹复制到Unit…

Matlab仿真径向受压圆盘光弹图像

Matlab仿真径向受压圆盘光弹图像-十步相移法 主要参数 % 定义圆盘参数 R 15; % 圆盘半径&#xff0c;单位&#xff1a;mm h 5; % 圆盘厚度&#xff0c;单位&#xff1a;mm P 300; % 径向受压载荷大小&#xff0c;单位&#xff…

基于Django的学校智能图书馆借书归还订阅管理系统

完整源码项目包获取→点击文章末尾名片&#xff01;

【设计模式-2】23 种设计模式的分类和功能

在软件工程领域&#xff0c;设计模式是解决常见设计问题的经典方案。1994 年&#xff0c;Erich Gamma、Richard Helm、Ralph Johnson 和 John Vlissides&#xff08;四人帮&#xff0c;GoF&#xff09;在《设计模式&#xff1a;可复用面向对象软件的基础》一书中系统性地总结了…

阿里云代理商热销产品推荐

在数字化浪潮的推动下&#xff0c;企业对于云计算的依赖日益加深。阿里云&#xff0c;作为中国领先的云计算服务提供商&#xff0c;为企业提供了丰富多样的云产品和服务。本文将聚焦于阿里云代理商热销产品推荐&#xff0c;探讨其如何帮助企业高效利用云资源&#xff0c;加速数…

[redux] 异步逻辑的两种写法

createAsyncThunk | Redux Toolkit 第一种, extraReducers 普通的reducers只能写同步代码 异步必须得用中间件的形式,就是异步代码调用完有结果了, 再调用同步的reducer, 大概这么理解, 第一种怎么用呢? 先用一个异步函数 const fetchUserById createAsyncThunk(users/fet…

在Java中使用有符号类型模拟无符号整数的技巧

有符号整数和无符号整数 有符号整数&#xff1a;可以表示正数、负数和零。例如&#xff0c;Java中的 byte 类型是有符号的&#xff0c;其范围是 -128 到 127.无符号整数&#xff1a;只能表示非负数&#xff08;即零和正数&#xff09;。例如&#xff0c;无符号 byte 应该表示的…

51单片机——8*8LED点阵

LED 点阵的行则为发光二极管的阳极&#xff0c;LED 点阵的列则为发光二极管的阴极 根据 LED 发光二极管导通原理&#xff0c;当阳极为高电平&#xff0c;阴极为低电平则点亮&#xff0c;否则熄灭。 因此通过单片机P0口可控制点阵列&#xff0c;74HC595可控制点阵行 11 脚 SR…

Flutter:邀请海报,Widget转图片,保存相册

记录下&#xff0c;把页面红色区域内的内容&#xff0c;转成图片后保存到相册的功能 依赖 # 生成二维码 qr_flutter: ^4.1.0 # 保存图片 image_gallery_saver_plus: ^3.0.5view import package:demo/common/index.dart; import package:ducafe_ui_core/ducafe_ui_core.dart; i…

C++Primer const限定符

欢迎阅读我的 【CPrimer】专栏 专栏简介&#xff1a;本专栏主要面向C初学者&#xff0c;解释C的一些基本概念和基础语言特性&#xff0c;涉及C标准库的用法&#xff0c;面向对象特性&#xff0c;泛型特性高级用法。通过使用标准库中定义的抽象设施&#xff0c;使你更加适应高级…

C语言 游动的小球

代码如下&#xff1a; 在这里插入代码片#include<stdio.h> #include<stdlib.h> #include<windows.h>int main() {int i,j;int x 5;int y 10;int height 20;int velocity_x 1;int velocity_y 1;int left 0;int right 20;int top 0;int bottom 10;while(1){…

动漫推荐系统django+vue前台后台完整源码

完整源码项目包获取→点击文章末尾名片&#xff01;

Chapter 1 Understanding Large Language Models

文章目录 Understanding Large Language ModelsWhat is an LLM?Applications of LLMSStages of building and using LLMsUsing LLMS for different tasksA closer look at the GPT architectureBuilding a large language modelSummary Understanding Large Language Models …

什么是VLAN?

VLAN&#xff08;Virtual Local Area Network&#xff0c;虚拟局域网&#xff09;是一种将物理局域网划分成多个逻辑上独立的虚拟网络的技术。VLAN不依赖于设备的物理位置&#xff0c;而是通过逻辑划分&#xff0c;将局域网内的设备虚拟地组织到同一组。这种技术允许网络管理员…

【君正T31开发记录】12.编译工具相关总结及介绍

移植交叉工具包的时候&#xff0c;发现这是很多工具的集合包&#xff1b;以及写makefile的时候&#xff0c;也需要了解下这些工具的作用及用法&#xff0c;这里总结记录一下常见的工具及相关用法。 g C编译器&#xff0c;用于编译C源代码文件&#xff0c;这个很常见&#xff0…

Appium(一)--- 环境搭建

一、Android自动化环境搭建 1、JDK 必须1.8及以上(1) 安装&#xff1a;默认安装(2) 环境变量配置新建JAVA_HOME:安装路径新建CLASSPath%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar在path中增加&#xff1a;%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin&#xff1b;(3) 验证…

猫的眼睛有几种颜色?

在猫咪神秘而迷人的世界里&#xff0c;它们的眼睛犹如璀璨星辰&#xff0c;闪烁着各异的光芒&#xff0c;颜色丰富多样&#xff0c;令人着迷。 猫眼睛的颜色&#xff0c;粗略一数&#xff0c;常见的便有黄色、蓝色、绿色、棕色&#xff0c;还有那神秘的异瞳。这些色彩并非无端生…

PHP框架+gatewayworker实现在线1对1聊天--接收消息(7)

文章目录 接收消息的原理接收消息JavaScript代码 接收消息的原理 接收消息&#xff0c;就是接受服务器转发的客户端消息。并不需要单独创建函数&#xff0c;因为 ws.onmessage会自动接收消息。我们需要在这个函数里进行处理。因为初始化的时候&#xff0c;已经处理的init类型的…

校园周边美食探索及分享平台的设计与实现(源码+数据库+文档)

亲测完美运行带论文&#xff1a;文末获取源码 文章目录 项目简介&#xff08;论文摘要&#xff09;运行视频包含的文件列表&#xff08;含论文&#xff09;前台运行截图后台运行截图 项目简介&#xff08;论文摘要&#xff09; &#xff1a; 美食一直是与人们日常生活息息相关…