C++基础之默认成员函数(构造函数,析构函数)

news2024/11/18 15:45:29

目录

空类中都有什么

默认成员函数

构造函数

简介

特性

注意

总结

析构函数

简介

特性

注意

总结


空类中都有什么

先看下面一段代码:

class Date
{

};

int main()
{
	Date d1;
	std::cout << sizeof(Date) << std::endl;
	std::cout << sizeof(d1) << std::endl;
	system("pause");
	return 0;
}

 代码运行结果为1,可是类中没有任何东西,为什么结果是1呢?

这是因为1字节是占位的,用来标记类,同时空类中并不是什么都没有,而是有编译器生成的6个默认函数。

默认函数就是,用户没有显示生成,编译器会默认生成的函数;

默认成员函数

函数名作用
构建函数对类进行初始化
析构函数对类进行清理
拷贝构建函数用同类对象进行类的初始化及创建
赋值重载函数赋值重载主要是把一个对象赋值给另一个对象
const成员函数
取地址函数

构造函数

简介

构造函数是一个特殊的成员函数,函数名与类名相同,没有返回值,创建类对象时由编译器自动调用,保证给每个类有一个合适的值,在整个函数的生命周期只调用一次;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
};

int main()
{
	Date _d1;
	system("pause");
	return 0;
}

特性

1、构造函数的函数名与类名相同;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
};

上面的代码中,构造函数名与类名相同都是Date;

2、没有返回值;

构造函数没有返回值,注意void(空类型)是一种返回值类型,构造函数连void也没有;

3、创建对象时由编译器自动调用;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
};

int main()
{
	Date _d1;
	system("pause");
	return 0;
}
_d1创建后结果为个变量为1;
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
};

int main()
{
	Date _d1(2023,10,12);
	system("pause");
	return 0;
}
_d1创建时传值,创建后各变量分别为2023,10,12
//无参创建对象的格式:
//传递参数创建时,格式为Date _d1(2023,10,11);
//为什么不传参时不用Date _d1();格式呢?
class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
};

int main()
{
	Date _d1();
	system("pause");
	return 0;
}

经过测试,该代码可以运行,但是对象不会创建;

这是因为 Date _d1()的语句并不是创建,而是函数声明其中Date是函数返回值,_d1是函数名;

4、构造函数可以重载;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year , int month , int day )
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	
};

int main()
{
	Date _d1;
	system("pause");
	return 0;
}
//代码运行的结果_d1中的变量都为1

注意:构造函数 可以重载,但是如果写的不当,调用时会产生二义性;

class Date
{
private:
	int _year;
	int _month;
	int _day;
public:
	Date(int year=1 , int month=1 , int day=1 )
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date()
	{
		_year = 1;
		_month = 1;
		_day = 1;
	}
	
};

int main()
{
	Date _d1;
	system("pause");
	return 0;
}

5、如果类中没有显式定义,那么编译器会生成一个无参的默认构造函数,如果显式定义,那么不会生成; 

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

int main()
{
	Date _d1;
	system("pause");
	return 0;
}

注意:编译器生成的构造函数,不会将内置类型的值初始化,各变量的值还是乱码; 

显示生成构造函数,编译器后都不会再生成默认构造函数;

class Time
{
public:
	Time()
	{
		std::cout << "Time()" << std::endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year=2023;
	int _month=2;
	int _day=2;
	// 自定义类型
	Time _t;
public:
	Date(int year , int month , int day )
	{
		_year = year;
		_month = month;
		_day = day;
	}
};
int main()
{
	Date d;
	system("pause");
	return 0;
}

  

6、内置类型编译器生成的默认成员函数没有用,自定义类型会调用它的默认成员函数;

class stack
{
private:
	int * _data;
	int _size;
	int _capacity;
public:
	stack(int capacity=4)
	{
		int *newdata = (int *)malloc(sizeof(int)*capacity);
		if (newdata == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_data = newdata;
		_capacity = capacity;
		_size = 0;
	}
	~stack()
	{
		free(_data);
		_data = nullptr;
		_capacity = 0;
		_size = 0;
	}
};

class queue
{
private:
	stack _stpush;
	stack _stpop;
};

int main()
{
	queue _q1;
	system("pause");
	return 0;
}

 

上面的函数中, queue对象没有创建构建函数,但创建时还是进行了初始化,整个过程如下queue对象创建时,编译器生成了默认的成员函数,queue没有内置类型,所以其默认构造函数调用了stack的默认构造函数(不能调用构造函数,因为stack的构造函数没有参数)完成queue对象的创建,为了重复整个过程可以在queue中加入内置类型:

class Time
{
public:
	Time()
	{
		std::cout << "Time()" << std::endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year;
	int _month;
	int _day;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	system("pause");
	return 0;
}

可以看到_t的内容调用了默认构造函数进行初始化,但是d的部分没有,但有一个有趣的情况:

class stack
{

public:
	stack(int capacity=4)
	{
		_num = (int *)malloc(sizeof(int)*capacity);
		if (_num == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;
	}
private:
	int * _num;
	int _size;
	int _capacity;
};


class queue
{
private:
	
	stack _s1;
	stack _s2;
	stack _s3;
	int _l;
};

int main()
{
	queue _q1;
	system("pause");
	return 0;
}

 在加入了malloc后,_l的值会被初始化,这是因为什么呢? 

7、无参构造函数,全缺省构造函数,编译器生成的默认构造函数,才是默认构造函数(默认构造函数不需要传参)

注意

C++11 中针对内置类型成员不初始化的缺陷,又打了补丁,即:内置类型成员变量在类中声明时可以给默认值:

class Time
{
public:
	Time()
	{
		std::cout << "Time()" << std::endl;
		_hour = 0;
		_minute = 0;
		_second = 0;
	}
private:
	int _hour;
	int _minute;
	int _second;
};
class Date
{
private:
	// 基本类型(内置类型)
	int _year=2023;
	int _month=2;
	int _day=2;
	// 自定义类型
	Time _t;
};
int main()
{
	Date d;
	system("pause");
	return 0;
}

在Date类中给3个内置变量默认值,创建后d中的3个变量自然被赋值,但加上默认构建函数后,默认值没有用;

class Date
{
private:
	// 基本类型(内置类型)
	int _year=2023;
	int _month=2;
	int _day=2;
	// 自定义类型
	Time _t;
public:
	Date(int year = 2022, int month = 10, int day = 19)
	{
		_year = year;
		_month = month;
		_day = day;
	}
};

 

总结

构建函数就是对类进行初始化的函数,它有七个特点:

一、函数名就是类名;

二、没有返回值;

三、构建函数可以重载(可以有很多个),但是默认构建函数不行(有且只能有一个);

四、创建对象时由编译器自动调用(有参数时创建方式 Date d(2,1,3),无参时创建方式Date d;不可以写成Date d();这样会被编译器视为函数声明); 

五、如果没有显式定义构造函数,编译器会自动生成默认构造函数,如果显示定义构造函数(无论是否默认构造函数)编译器不生成默认构造函数;

六、编译器生成的默认构造函数,内置类型不会初始化,自定义类型,编译器会调用其默认函数;

七、默认构造函数一定是无参的,如果有参就不是默认构造函数;

析构函数

简介

析构函数的作用与构造函数相反,是类销毁时,完成对象中资源清理工作,它不是销毁对象,销毁对象的工作由编译器完成,它类似于destory,清理对象占据的资源。

class Date
{
private:
	int _year=1;
	int _month = 2;
	int _day = 3;
public:

};


void func()
{
	Date _d;
}
int main()
{
	func();
	system("pause");
	return 0;
}

特性

1、析构函数的函数名是~类名;

2、析构函数没有返回值没有参数;

class stack
{
private:
	int * _data;
	int _size;
	int _capacity;
public:
	stack(int capacity = 4)
	{
		_data = (int *)malloc(sizeof(int)*capacity);
		if (_data == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_capacity = capacity;
		_size = 0;
	}
	~stack()
	{
		free(_data);
		_data = nullptr;
		_capacity = 0;
		_size = 0;
	}
};

void func()
{
	stack st;
}
int main()
{
	func();
	system("pause");
	return 0;
}

3、析构函数只能有一个,不能重载;

析构函数只能有一个,由C++规则决定不可以重载,如果写两个析构函数编译时会报错。

4、析构函数没有显示定义时,系统会自动生成;

但是编译器生成析构函数对内置类型和自定义类型都不会进行清理,会造成数据泄漏;

//编译器生成析构函数的情况
class stack
{
//private:
	
public:
	stack(int capacity=4)
	{
		_data = (int *)malloc(sizeof(int)*capacity);
		if (_data == nullptr)
		{
			perror("malloc fail");
			exit(-1);
		}
		_size = 0;
		_capacity = 0;
	}

	int* _data;
	int _size;
	int _capacity;
};



void func()
{
	stack s;
	s._data[0] = 1;
	s._data[1] = 2;
	s._data[2] = 3;
	s._data[3] = 4;
}

int main()
{
	func();
	int a=10;
	system("pause");
	return 0;
}

stack对象在func中创建,所以出了func该对象就会调用析构函数;

func调用完成后,对象中动态开辟空间中的数据没有被清理;

显示定义析构函数:

	~stack()
	{
		if (_data != nullptr)
		{
			free(_data);
			_data = nullptr;
			_size = 0;
			_capacity = 0;
		}
	}

 经过显示定义析构函数,func调用完成后,动态开辟空间中的值被销毁;

5、析构函数对自定义成员类型会调用它的析构函数;

接下来先套个娃,创建一个queue类,其成员属性为stack,不创建析构函数:

class queue
{
public:
	stack s;
};

void func()
{
	queue q;
	q.s._data[0] = 1;
	q.s._data[1] = 2;
	q.s._data[2] = 3;
	q.s._data[3] = 4;
}

当func调用结束,对象q中动态开辟空间中的数据已经被销毁,虽然queue没有创建析构函数,但是编译器对自定义类型会调用它的析构函数,所以空间中的数据才会被销毁; 

6、类中存在资源时,一定要写析构函数,不然会造成内存泄漏(内存泄漏是指向动态开辟空间中的数据没有被销毁);

注意

析构函数在使用时,自定义类型一定要写析构,不然会造成内存泄漏,对内置类型来说,其值由编译器控制,可以不写;

总结

什么情况下需要构建与析构呢,下面分别以日期类,栈类和队列类为例:

//日期类
class Date
{
private:
	int _year;
	int _month;
	int _day;
};

//栈类
class stack
{
private:
	int * _data;
	int _size;
	int _capacity;
};

//日期类
class queue
{
private:
	stack _s1;
	stack _s2;
};
日期类栈类队列类
构建需要默认构建函数对内置类型没作用,需要显式实现需要栈类本质都是自定义类型,开辟动态空间需要构建函数定义不需要自定义类型调用栈类型的默认构建函数实现
析构不需要内置类型由编译器处理需要存在动态开辟空间需要析构函数销毁相关数据不需要调用栈类型的析构函数

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

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

相关文章

Linux之系统基本设置(四)

1、Linux 系统基本设置 1、系统时间管理 查看系统当前时间和时区 [root192 ~]# date 2023年 05月 04日 星期四 22:43:16 EDT [root192 ~]# date -R Thu, 04 May 2023 22:43:24 -0400 [root192 ~]# date %Y %m %d %H:%M:%S 2023 05 04 22:43:38设置完整时间 [root192 ~]# da…

基于html+css的图展示67

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

Shell脚本文本三剑客之sed编辑器(拥明月入怀,揽星河入梦)

文章目录 一、sed编辑器简介二、sed工作流程三、sed命令四、sed命令的使用1.sed打印文件内容&#xff08;p&#xff09;&#xff08;1&#xff09;打印文件所有行&#xff08;2&#xff09;打印文件指定行 2.sed增加、插入、替换行&#xff08;a、i、c&#xff09;(1&#xff0…

【C++】类和对象()

&#x1f601;作者&#xff1a;日出等日落 &#x1f514;专栏&#xff1a;C 当你的希望一个个落空&#xff0c;你也要坚定&#xff0c;要沉着! —— 朗费罗 前言 面向过程和面向对象初步认识 C语言是面向过程的&#xff0c;关注…

矿井水除氟系统CH-87的技术详解

今天&#xff0c;文章中会谈到的问题是关于煤化工废水深度处理除氟、总氮、砷等污染物工艺技术的拆解分析&#xff0c;用什么样的工艺技术能把矿井水中的氟、砷、总氮做到1个毫克升以下的标准符合达标排放&#xff1f;希望能对相关行业起到一定的帮助作用。我国是一个资源丰富的…

【开源项目】Disruptor框架介绍及快速入门

Disruptor框架简介 Disruptor框架内部核心的数据结构是Ring Buffer&#xff0c;Ring Buffer是一个环形的数组&#xff0c;Disruptor框架以Ring Buffer为核心实现了异步事件处理的高性能架构&#xff1b;JDK的BlockingQueue相信大家都用过&#xff0c;其是一个阻塞队列&#xf…

视觉错觉图像可逆信息隐藏

—————————————————————————————————————————————————————————— 文献学习&#xff1a;视觉错觉图像可逆信息隐藏 [1] Jiao S , Jun F . Image steganography with visual illusion[J]. Optics Express, 2021, 29(10…

【算法与数据结构】栈

栈 栈&#xff1a;结构定义 放入元素是从底向上放入 有一个栈顶指针&#xff0c;永远处在栈顶的元素 还需要标记栈大小的size 栈的性质&#xff1a; Fisrt-in Last-out (FILO) 先进后出 栈改变元素的顺序 栈&#xff1a;出栈 让栈顶指针向下移动一位 栈&#xff1a;入栈 …

【JavaEE】SpringMVC

目录 SpringMVC 获取连接 RequestMapping / GetMapping... 获取参数 获取querystring中的参数(获取表单数据基本相同) 获取URL中的参数 获取JSON对象 获取文件(通过表单) 获取Cookie 获取Header 获取Session 返回数据 返回数据 返回JSON对象 返回静态页面 请求…

云渲染时可以关机吗_云渲染电脑可以关闭吗?

云渲染可简单理解为放在云端的渲染农场&#xff0c;可区别于用户本地自己搭建的小型私有农场&#xff0c;用户只需将自己制作好的项目文件进行打包&#xff0c;通过 云渲染平台提供的客户端或网页端将文件上传到云端进行渲染。很多用户通过云渲染作业&#xff0c;解放了自己本地…

深耕5G+AIoT产业赛道,2023高通&美格智能物联网技术开放日隆重举行

5月11日&#xff0c;高通技术公司携手美格智能联合举办了“高通&美格智能物联网技术开放日”深圳站活动。大会现场&#xff0c;智能物联网行业合作伙伴齐聚一堂&#xff0c;围绕5GAIoT前沿技术&#xff0c;通过大咖专业的技术分享、落地应用介绍和现场丰富的产品展示&#…

Pytorch nn.Softmax(dim=?) 详解

本文参考自&#xff1a;Pytorch nn.Softmax(dim?) - 知乎 原文写得很好了&#xff0c;我这边另外完善了一些细节&#xff0c;让大家理解地更加直白一些。 可以先去看上面的参考文章&#xff0c;也可以直接看我这篇。 目录 1、tensor1 1&#xff09;已知该矩阵的维度为&am…

vue实现聊天框自动滚动

需求 1、聊天数据实时更新渲染到页面 2、页面高度随聊天数据增加而增加 3、竖向滚动 4、当用户输入聊天内容或者接口返回聊天内容渲染在页面后&#xff0c;自动滚动到底部 5、提供点击事件操控滚动条上下翻动 环境依赖 vue&#xff1a;vue…

两小时搭建属于自己的chatGPT(ChatGLM)免硬件(白嫖)

目录 准备&#xff08;注册&#xff09;: 搭建: API模式: 测试&#xff1a; 总结&#xff1a; 准备&#xff08;注册&#xff09;: 注册modelscope(白嫖)免费使用服务器 https://modelscope.cn/ 按照图片里的选择(选择其他好像不能创建成功) 可以白嫖60多个小时的配置 8…

Java 8 Time 关于java.time包中你可能不知道的使用细节

目录 前言一、时区与时间1. 世界标准时&#xff1a;UTC、GMT、UT2. 地区时&#xff1a;Asia/Shanghai、UTC83. 时区&#xff1a;ZoneId、TimeZone4. 时间偏移量&#xff1a;ZoneOffset5. 时区简称&#xff1a;CTT、PRC 二、主要时间类1. 重要时间接口&#xff1a;Temporal2. 时…

【CocosCreator入门】CocosCreator组件 | Collider(碰撞)组件

Cocos Creator是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中碰撞系统组件是该引擎的重要组成部分。该组件可用于检测游戏中各个元素之间的碰撞&#xff0c;例如玩家角色与敌人、子弹与障碍物等。 目录 一、组件介绍 二、组件属性 2.1BoxCollid…

基于SpringBoot+微信小程序的农产品销售平台

基于SpringBoot微信小程序的农产品销售平台 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目…

Test Doubles测试替身: Testing in Distributed Systems and Real World

什么是Test Doubles In software testing, we developed unit tests and integration tests to test the codes functionality. However, in the real world, it is very common for a piece of code to interact with external components, for example, databases, public A…

【人工智能概论】pyplot作图中文显示、逐点坐标显示、保存图像

【人工智能概论】pyplot作图中文显示、逐点标记、保存图像 文章目录 【人工智能概论】pyplot作图中文显示、逐点标记、保存图像一. 简单的绘图二. 逐点坐标显示三. 中文显示四. 中文显示可能遇到的问题——缺少字体4.1 下载 SimHei.ttf4.2 复制 SimHei.ttf 到 Matplotlib 的 fo…

好的Robots.txt设计对Google收录有很大的帮助

Robots.txt 文件是用于指导搜索引擎爬虫在网站上爬行的标准。正确地设计 Robots.txt 文件可以帮助 Google 爬虫更好地理解您的网站结构&#xff0c;从而提高您的网站在 Google 搜索引擎上的收录率。 以下是一些设计 Robots.txt 文件的技巧&#xff0c;可以帮助 Google 爬虫更好…