【c++】类和对象(三)构造函数和析构函数

news2024/11/15 7:19:11

Alt

🔥个人主页:Quitecoder

🔥专栏:c++笔记仓

Alt

朋友们大家好,本篇文章我们带来类和对象重要的部分,构造函数和析构函数

目录

  • 1.类的6个默认成员函数
  • 2.构造函数
    • 2.1构造函数其他特性
  • 3.构析函数
    • 3.1特性:

1.类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类

任何类在什么都不写时,编译器会自动生成以下6个默认成员函数(用户没有显式实现,编译器会生成的成员函数称为默认成员函数)

class Date {};

在这里插入图片描述

2.构造函数

我们看下面这个类

class Date
{
public:
	void Init(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;
	d1.Init(2005, 6, 23);
	d1.Print();
	Date d2;
	d2.Init(2024, 3, 25);
	d2.Print();
	return 0;
}

对于Date类,可以通过 Init 公有方法给对象设置日期,但如果每次创建对象时都调用该方法设置信息,未免有点麻烦,并且容易忘记那能否在对象创建时,就将信息设置进去呢?

构造函数是一种特殊的成员函数,它在创建对象时自动调用,其主要目的是初始化对象。在C++中,构造函数具有与其所属类相同的名称,并且没有返回类型。构造函数可以有参数,也可以没有参数,允许通过不同的方式初始化对象的成员变量。如果一个类定义中没有显式地包含任何构造函数编译器会自动生成一个默认构造函数(只在没有其他任何构造函数时)

特性:

  1. 函数名与类名相同
  2. 无返回值
  3. 对象实例化时编译器自动调用对应的构造函数
  4. 构造函数可以重载

那么上面所示的代码构造函数如何写呢?如下:

class Date
{
public:
	Date()
	{
		_year = 1;
		_month = 2;
		_day = 3;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;	
	d1.Print();
	
	return 0;
}

这种是不带参数的
在这里插入图片描述
在我们进行实例化 Date d1;时,自动调用构造函数完成初始化,我们可以用汇编代码进行查看:
在这里插入图片描述

我们也可以在其中加入带参数的构造函数,实现函数重载

class Date
{
public:
	Date()
	{
		_year = 1;
		_month = 2;
		_day = 3;
	}

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

我们知道,构造函数是实例化调用的,那么如何实现带参数构造函数呢?

代码如下:

 Date d1; // 调用无参构造函数
 Date d2(2005, 1, 1); // 调用带参的构造函数

注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明

在这里插入图片描述

Date d3();

能不能这样定义呢?
在这里插入图片描述
这里编译错误,即这里并不能与函数的声明区分开,所以书写格式严格按照上述方法来写

如果我们将第一个无参格式屏蔽掉呢?

class Date
{
public:
	/*Date()
	{
		_year = 1;
		_month = 2;
		_day = 3;
	}*/

	Date(int year, int month, int day)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1;	
	d1.Print();
	return 0;
}

在这里插入图片描述
代码中出现错误的原因在于,为Date类定义了一个接收三个参数的构造函数,但是没有定义默认构造函数(无参数构造函数)。接着,在main函数中,尝试使用无参数的方式构造d1对象:Date d1;。这在类定义中是非法的,因为一旦定义了自己的构造函数(不管有多少参数),C++编译器就不会自动生成默认构造函数

我们这里也可以通过缺省参数来实现:
在这里插入图片描述
在这里插入图片描述

十分好用

2.1构造函数其他特性

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

class Date
{
public:
	/*
	// 如果用户显式定义了构造函数,编译器将不再生成
	Date(int year, int month, int day)
	{
	_year = year;
	_month = month;
	_day = day;
	}
	*/

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

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

我们来试试默认生成的函数:

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

在这里插入图片描述
这个默认生成的函数并没有做什么事情

我们可能会产生疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??

C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char…,自定义类型就是我们使用class/struct/union等自己定义的类型,默认生成的构造函数,对内置类型不做处理,自定义类型会去调用它的默认构造函数

我们看下面这串代码:

class A
{
public:
	A()
	{
		cout << "A()" << endl;
		_a = 0;
	}
private:
	int _a;
};
	
class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
  //内置类型
	int _year ;
	int _month ;
	int _day;
  //自定义类型
	A _aa;
};
int main()
{
	Date d1;
	return 0;
}

默认生成的构造函数,对内置类型不做处理,自定义回去调用他的默认构造
在这里插入图片描述
我们发现调用了A的构造
在这里插入图片描述

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

class Date
{
public:
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}	
private:		
	int _year =1;
	int _month =2;
	int _day;
	A _aa;
};

在声明的位置给缺省值在这里还是声明
在这里插入图片描述

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为是默认构造函数

思考下面代码能否编译成功?

class Date
{
public:
	Date()
	{
		_year = 1900;
		_month = 1;
		_day = 1;
	}
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
private:
	int _year;
	int _month;
	int _day;
};
// 以下测试函数能通过编译吗?
int main()
{
	Date d1;
	return 0;
}

答案是不可以

Date类中定义了两个构造函数,看起来目的是提供一个默认构造函数和一个带默认参数值的构造函数。然而,这里的设计存在冲突,因为两个构造函数都可以作为默认构造函数,这导致了一个重定义的问题

在C++中,如果构造函数的所有参数都有默认值,它就可以被视为无参数调用时的候选构造函数,也就是说,它可以被当作默认构造函数。因此,这个类设计在逻辑上等同于提供了两个默认构造函数,这在C++中是不允许的,会导致编译错误

问题在于,当尝试创建一个不传递任何参数的Date对象(如Dated1;),编译器将无法确定应该调用哪个构造函数,因为两个构造函数都满足调用条件

3.构析函数

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?

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

3.1特性:

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成**默认的析构函数。**注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数

写法如下:

typedef int DataType;
class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		_array = (DataType*)malloc(sizeof(DataType) * capacity);
		if (NULL == _array)
		{
			perror("malloc申请空间失败!!!");
			return;
		}
		_capacity = capacity;
		_size = 0;
	}
	void Push(DataType data)
	{
		// CheckCapacity();
		_array[_size] = data;
		_size++;
	}
	// 其他方法...
	~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}
private:
	DataType* _array;
	int _capacity;
	int _size;
};
int main()
{
	Stack st1;
	st1.Push(1);
	st1.Push(2);

	return 0;
}

其中:

~Stack()
	{
		if (_array)
		{
			free(_array);
			_array = NULL;
			_capacity = 0;
			_size = 0;
		}
	}

为析构函数,我们定义一个栈,如果不写析构函数,则会发生内存泄漏

在这里插入图片描述

c语言中,我们主动调用Destroy函数

关于编译器自动生成的析构函数,是否会完成一些事情呢?下面的程序我们会看到,编译器生成的默认析构函数,对自定类型成员调用它的析构函数

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

在这里插入图片描述
在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month,
_day三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数

但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁

main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类

本节内容到此结束!感谢大家观看!!!

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

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

相关文章

【JavaSE】继承和多态

目录 前言 1. static 1.1 static修饰成员变量 1.2 类变量的使用场景 1.3 static修饰成员方法 2. 代码块 3. 继承 3.1 为什么会有继承 3.2 继承的语法 3.3 继承后成员的访问 super关键字 3.4 子类的构造器 super和this的联系 继承中的代码块 4. 多态 4.1 多态的…

翻过DP这座大山

1.AcWing 跳台阶 第一种方法:暴力搜索DFS #include <iostream> using namespace std;int dfs(int n) {if(n 1) return 1;else if(n 2) return 2;else return dfs(n-1)dfs(n-2); }int main() {int x; cin>>x;cout<<dfs(x)<<endl;return 0; }显然如…

hcia datacom课程学习(3):http与https、FTP

1.超文本传输协议&#xff1a;http与https &#xff08;1&#xff09;用来访问www万维网。 wwwhttp&#xff0b;html&#xff0b;URLweb &#xff08;2&#xff09;它们提供了一种发布和接受html界面的方法&#xff1a;当在网页输入URL后&#xff0c;从服务器获取html文件来…

3种货币对保证金和杠杆关系,众汇实例分享

在外汇交易中货币对总共分为3种&#xff1a;主要货币对、交叉货币对和新兴市场货币对&#xff0c;这3种不同的货币对保证金和杠杆的关系各自不同&#xff0c;今天众汇外汇实例分享。 1.直接引用 直接报价是美元在分数中处于第二位的外汇汇率。 保证金持仓量*合约规模/杠杆*开…

QT常见Layout布局器使用

布局简介 为什么要布局&#xff1f;通过布局拖动不影响鼠标拖动窗口的效果等优点.QT设计器布局比较固定&#xff0c;不方便后期修改和维护&#xff1b;在Qt里面布局分为四个大类 &#xff1a; 盒子布局&#xff1a;QBoxLayout 网格布局&#xff1a;QGridLayout 表单布局&am…

软件测试|使用selenium进行多窗口操作

简介 在我们进行自动化测试的工作中&#xff0c;经常会点击某个元素或者链接就会自动打开一个新页面&#xff0c;需要我们转到新打开的页面去进行操作&#xff0c;这个时候我们就需要能够自动切换到新页面进行后续的操作&#xff0c;selenium同样支持这个功能&#xff0c;本文…

CSS设置移动端页面底部安全距离

env(safe-area-inset-bottom)是一个CSS属性值&#xff0c;用于设置底部安全距离。它表示使用环境变量来获取底部安全距离的值。当使用环境变量时&#xff0c;需要使用env()函数来引用具体的环境变量。例如&#xff1a; <style> .box{padding-bottom: env(safe-area-inse…

Java 算法和数据结构 答案整理,最新面试题

Java中如何使用动态规划求解背包问题&#xff1f; 1、定义子问题&#xff1a; 首先确定动态规划状态&#xff0c;通常以物品数量和背包容量为变量定义子问题&#xff0c;例如dp[i][j]表示前i件物品放入容量为j的背包所能获得的最大价值。 2、确定状态转移方程&#xff1a; 基…

​ 翻译 《The Old New Thing》

今天开始&#xff0c;为大家翻译微软优秀的技术专栏 The Old New Thing。 由微软高级工程师 Raymond Chen 撰写。该专栏起初是一个博客&#xff0c;后来也出版了同名书籍。专栏主要围绕 Windows 操作系统的开发和设计展开&#xff0c;涵盖了 Windows 平台的历史、技术细节、编程…

maven的依赖继承

先说一下创建子maven工程的步骤 继承 继承的作用&#xff1a;在父工程中&#xff0c;统一管理项目中的依赖信息&#xff0c;进行统一的版本控制 继承的背景是&#xff1a;对一个大型的项目进行了模块拆分&#xff0c;一个project下&#xff0c;创建了很多的module&#xff0c…

机器学习——随机森林

机器学习——随机森林 随机森林是一种强大的集成学习算法&#xff0c;能够用于分类和回归任务。它基于决策树构建&#xff0c;在集成学习框架下&#xff0c;通过Bagging算法和随机特征选择的方式&#xff0c;将多棵决策树组合成一个更强大的模型。本篇博客将介绍随机森林的原理…

计算机专业大学生应该在大学四年踏实学哪些东西?

在这个信息爆炸的时代&#xff0c;对于我们计算机专业的大学生来说&#xff0c;真正难的不是学习新技术&#xff0c;而是决定学什么、放弃什么。时间毕竟有限&#xff0c;每一个选择都像是在做一笔投资&#xff0c;希望能够收获最大的回报。我在这里分享一下我的一些经验&#…

服务器被攻击有什么表现?

引言 在现今高度互联的网络环境中&#xff0c;服务器安全已成为每个企业和个人站长不容忽视的重要议题。服务器作为承载关键业务和数据的核心设施&#xff0c;一旦遭受攻击&#xff0c;不仅可能导致服务中断、数据泄露&#xff0c;还可能带来严重的经济损失和声誉损害。本文旨…

【网络建设与运维】2024年河北省职业院校技能大赛中职组“网络建设与运维”赛项规程

培训、环境、资料、考证 公众号&#xff1a;Geek极安云科 网络安全群&#xff1a;775454947 网络系统管理群&#xff1a;223627079 网络建设与运维群&#xff1a;870959784 极安云科专注于技能提升&#xff0c;赋能 2024年广东省高校的技能提升&#xff0c;在培训中我们的应急…

unity 多屏幕操作

想了解基础操作请移步&#xff1a;&#xff08;重点是大佬写的好&#xff0c;这里就不再赘述&#xff09; Unity 基础 之 使用 Display 简单的实现 多屏幕显示的效果_unity display-CSDN博客 在panel上也可以通过获取 Canvas&#xff0c;来达到切换多屏幕的操作&#xff0c; …

详解多模态 AI

2022 年 11 月&#xff0c;OpenAI 推出了 ChatGPT。它只用了几天时间就以其前所未有的能力席卷了世界。生成式人工智能革命已经开始&#xff0c;每个人都在问同一个问题&#xff1a;下一步是什么&#xff1f; 当时&#xff0c;ChatGPT 和许多其他由大型语言模型 &#xff08;L…

【RK android7.1 开机进入主界面前短暂黑屏或者白屏问题】

RK android7.1 开机进入主界面前短暂黑屏或者白屏问题 问题描述解决方法郑重声明:本人原创博文,都是实战,均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip OS:Android 7.1.2 Kernel: 3.10 问题描述 开机进入主界面得一瞬间,launcher短暂黑屏或者白屏…

这回轮到鸿蒙禁用安卓了!!!

1月18日&#xff0c;鸿蒙生态千帆仪式上&#xff0c;华为正式宣布了HarmonyOS NEXT&#xff08;下简称鸿蒙星河版或纯血鸿蒙&#xff09;开发者预览已向开发者开放申请&#xff0c;纯血鸿蒙开始走向普及阶段。伴随着不再兼容安卓的纯血鸿蒙铺开&#xff0c;鸿蒙走进了运营属于自…

【活动预告】本周四(3月28日)AI算法大模型备案线上活动

Al算法备案中心特邀十年合规专家「乐歌」&#xff0c;于本周四进行线上算法备案活动 支持AI创业者&#xff0c;免费咨询算法备案 3.28日20&#xff1a;00腾讯会议欢迎参与&#xff01; 扫码添加活动助理报名参加&#xff01;

【Vue3之computed属性(四)】

文章目录 前言一、computed属性有缓存二、使用方法三、修改全名 前言 理解computed属性&#xff0c;实现输入姓和名得出全名并双向绑定&#xff0c;区分单向绑定和双向绑定。测试computed属性和方法的区别 一、computed属性有缓存 先引入computed&#xff0c;写箭头函数定义并…