C++奇迹之旅:构造函数和析构函数

请添加图片描述

文章目录

  • 📝类的6个默认成员函数
  • 🌠 构造函数
    • 🌉 概念
    • 🌉特性
    • 🌉三种默认构造函数
  • 🌠 析构函数
  • 🌠 特性
  • 🚩总结


📝类的6个默认成员函数

如果一个类中什么成员都没有,简称为空类
空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

class Date {};

🌠 构造函数

🌉 概念

对于以下Date类:

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(2006, 6, 27);d1.Print();Date d2;d2.Init(2022, 7, 6);d2.Print();return 0;
}

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

构造函数是一个特殊的成员函数名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次

🌉特性

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

  1. 函数名类名相同。
  2. 无返回值。
  3. 对象实例化时编译器自动调用对应的构造函数。
  4. 构造函数可以重载。
    构造函数可以重载,主要分为两类:
  • 无参构造函数
class Date
{
public://无参构造函数Date(){}void print(){cout << _year << _month << _day << endl;}private:int _year;int _month;int _day;
};int main()
{Date d1;//调用无参构造函数d1.print();return 0;
}

在这里插入图片描述
这样的无参构造,依然是初始化为随机数。
注意:如果无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明

在这里插入图片描述

  • 带参构造函数
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 d2(2024, 4, 15);//调用带参构造函数//带参是要直接在后面加括号的d2.print();return 0;
}

调用带参构造函数,带参是要直接在后面加括号的
在这里插入图片描述

两者结合,构成重载关系:在这里插入图片描述

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

没有显示构造函数在这里插入图片描述
代码可以通过编译,因为编译器生成了一个无参的默认构造函数,对象实例化,编译器调用自动调用默认构造函数,这多好啊,都不用自己实现,但是使用编译器实现的默认构造函数出来初始化的数据是随机值,不是0,这就有点麻烦了。
在这里插入图片描述

而如果我们显示实现呢?

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 d2(2024, 4, 15);//调用带参构造函数//带参是要直接在后面加括号的d2.print();return 0;
}

一旦自己显式定义任何构造函数,编译器将不再生成。
在这里插入图片描述

  1. 关于编译器生成的默认成员函数,很多童鞋会有疑惑:不实现构造函数的情况下,编译器会生成默认的构造函数。但是看起来默认构造函数又没什么用?d对象调用了编译器生成的默认构造函数,但是d对象_year/_month/_day,依旧是随机值。也就说在这里编译器生成的默认构造函数并没有什么用??(有些编译器可能会初始化为0,但是C++标准并没有规定)

解答:C++把类型分成内置类型(基本类型)和自定义类型。内置类型就是语言提供的数据类型,如:int/char...,自定义类型就是我们使用class/struct/union等自己定义的类型,看看
下面的程序,就会发现编译器生成默认的构造函数会对自定类型成员_t调用的它的默认构造函数。

让让我们看看以下这个代码:

class A
{
public:A(){_a = 0;cout << "A()" << endl;}private:int _a;
};class Date
{
public:// 我们没写,有没有构造函数?有->编译器自动生成// 内置类型/基本类型 int/char/double.../指针 // 自定义类型       class/struct...// 编译器自动生成构造函数,对于内置类型成员变量,没有规定要不要做处理!(有些编译器会处理)//                       对于自定义类型成员变量才会调用他的无参构造void Print(){cout << _year << "-" << _month << "-" << _day << endl;}private:int _year; // 年int _month; // 月int _day; // 日A _aa;
};int main()
{Date d1;d1.Print();return 0;
}

开始运行图:这些都是编译器给的随机值在这里插入图片描述
结束运行图:编译器对自定义类型成员变量去调用它的无参构造,对内置类型不做处理,当你编译器跑出了随机值或者0,也不要奇怪,不同编译器不同实现。
在这里插入图片描述
总结:如果在Time这个类中如果没有Time()成员函数初始化赋值,那继续像类Date这样自定义一个类型,那他会继续调用下一个自定义类的默认构造函数,如果后面的类还有,那么他就会不断查找下去,直到最后内置类型有初始化了,或者说没有自定义类型了。
总结一句话:自定义类型的终点还是内置类型

我们没写,有没有构造函数?有,编译器自动生成,内置类型/基本类型 int/char/double.../指针 ,对于自定义类型 class/struct...编译器自动生成构造函数,对于内置类型成员变量,没有规定要不要做处理!(有些编译器会处理)
对于自定义类型成员变量才会调用他的无参构造
我们再看这个代码:

class Time
{
public:Time(){cout << "Time()" << 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;return 0;
}

在这里插入图片描述
对于内置数据类型的成员变量,它们在默认构造函数中不会被初始化,所以它们的值是未定义的,也就是随机值。而对于自定义类型的成员变量,编译器会调用它们的默认构造函数来初始化,但是如果这个自定义类型也没有定义默认构造函数,那么它的成员变量也会是未定义的值,像Time()构造函数中,我们给他的成员初始化为0。

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

class Time
{
public:Time(){cout << "Time()" << endl;_hour = 0;_minute = 0;_second = 0;}
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;
}

这个给默认值不叫初始化,叫做给缺省值,因为在类中,内置类型里的这些是声明,不是定义,没有分配内存空间,所以不能赋值初始化。
在这里插入图片描述
小提示:
这里当我们调试时,按下F11它是直接跳转到time()的成员内部,那中间的内置类型不过一遍吗?因此我们再稍微改改,看看以下代码:

class Time
{
public:Time(int hour = 1, int minute = 1, int second = 1){cout << "Time()" << endl;_hour = hour;_minute = minute;_second = second;}
private:int _hour = 9;int _minute = 8;int _second = 7;
};

动态调试图:
请添加图片描述

这里我们看到他并不是跳过内置类型,然后直接调用自定义类型,真正过程是略过一下内置类型,然后在构造函数中用参数覆盖了内置类型的值。

那自动生成的构造函数意义何在?编译器生成的默认构造函数确实没有什么用,因为它并不会初始化类的成员变量。

如果在Time这个类中如果没有Time()成员函数初始化赋值,那继续像类Date这样自定义一个类型,那他会继续调用下一个自定义类的默认构造函数,如果后面的类还有,那么他就会不断查找下去,直到最后内置类型有初始化了,或者说没有自定义类型了。
总结一句话:自定义类型的终点还是内置类型

🌉三种默认构造函数

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

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 Test()
{Date d1;return 0;
}

在这里插入图片描述

这段代码不能通过编译。编译器会报错 “C2668: ‘Date::Date’: 对重载函数的调用不明确”。
出现这个错误的原因是,在 main() 函数中,当创建 Date d1 对象时,编译器无法确定应该调用哪个构造函数。
这是因为 Date() 构造函数和 Date(int, int, int) 构造函数都可以匹配 Date d1 的初始化,编译器无法确定应该调用哪个构造函数,产生调用歧义。因此我们可以使用一个即可,用的比较多的是这种全缺省构造函数:

Date(int year = 1900, int month = 1, int day = 1){_year = year;_month = month;_day = day;}

实践中总结:
1、一般情况构造函数都需要我们自己显示的去实现
2、只有少数情况下可以让编译器自动生成构造函数
类似MyQueue,成员全是自定义类型
在这里插入图片描述
通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?

🌠 析构函数

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

🌠 特性

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

  1. 析构函数名是在类名前加上字符 ~。
  2. 无参数无返回值类型。
  3. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
  4. 对象生命周期结束时,C++编译系统系统自动调用析构函数。
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三个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_tTime类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。
但是:main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Date类生成的默认析构函数

注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数

  1. 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。
typedef int DataType;class Stack
{
public:Stack(size_t capacity = 3){cout << "Stack(size_t capacity = 3)" << endl;_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(){cout << "~Stack()" << endl;if (_array){free(_array);_array = NULL;_capacity = 0;_size = 0;}}
private:DataType* _array;int _capacity;int _size;
};class MyQueue 
{
private:Stack _st1;Stack _st2;int _size = 0;
};int main()
{//Stack st;MyQueue q;return 0;
}

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

实践中总结:
1、有资源需要显示清理,就需要写析构。如:Stack List
2、有两种场景不需要显示写析构,默认生成就可以了a、没有资源需要清理,如:Dateb、内置类型成员没有资源需要清理。剩下都是自定义类型成员。如:MyQueue


🚩总结

请添加图片描述

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

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

相关文章

iOS重签名-超详细,附排错

文章目录 重签名步骤步骤 1: 准备必要的材料步骤 2: 解压 .ipa 文件步骤3:将 Provisioning Profile 复制到 Payload 目录步骤 4: 移除原来的签名步骤 5: 使用新的证书和 Provisioning Profile 进行重签名步骤 6: 重新打包 .ipa 文件步骤 7: 安装和测试得到provisioning file和…

Python编程玩转二维码

文章目录 Python编程玩转二维码第一部分&#xff1a;背景介绍第二部分&#xff1a;qrcode库是什么&#xff1f;第三部分&#xff1a;如何安装这个库&#xff1f;第四部分&#xff1a;库函数使用方法第五部分&#xff1a;场景应用第六部分&#xff1a;常见Bug及解决方案第七部分…

微信小程序展示倒计时

html <view class"countdown"> <text>倒计时&#xff1a;</text> <text wx:for"{{countdown}}" wx:key"index">{{item}}</text> </view> ts data: {countdown: [], // 存放倒计时数组 targetTime:…

【CSAPP/计组】#1 数的存储与表示方法、机器中浮点数加减法详解

文章目录 前言一、定点格式定点整数 二、浮点数的表示方法2.1 浮点数存储2.2 浮点数加减法a. 操作数检查b. 对阶c. 尾数相加与检查溢出d. 结果规格化与溢出处理e.舍入处理 Some tips: 为什么进位/双符号判断法有效的通俗解释&#xff1f;Some tips:符号位拓展 前言 计算机中常…

【归并】Leetcode 排序数组

题目讲解 912. 排序数组 算法讲解 使用归并算法排序数组&#xff0c;我们先在数组中寻找一个mid点&#xff0c;然后把数组分成了两部分&#xff0c;我们先排左部分&#xff0c;排左边部分的时候有需要将当前的子数组分成两部分&#xff0c;继续循环&#xff0c;直到当前子数组…

【结构型模式】装饰器模式

​一、装饰器模式概述 装饰器模式&#xff08;装饰者模式&#xff09;定义&#xff1a;装饰器模式动态地将责任附加到对象上。若要拓展功能&#xff0c;装饰者提供了比继承更有弹性地替代方案。&#xff08;对象结构型模型&#xff09;通俗点来说&#xff1a;动态的给一个对象增…

淘宝购物更智能:taobao.item_search API接口实现关键字精准匹配

随着电子商务的飞速发展&#xff0c;淘宝作为中国最大的网络购物平台之一&#xff0c;为亿万消费者提供了便捷、丰富的购物体验。然而&#xff0c;在海量商品中快速找到符合自己需求的商品&#xff0c;一直是消费者面临的挑战。为了提升购物体验&#xff0c;淘宝开放平台提供了…

【JS】js数字转k、w结尾 | 1000 = 1k

问题 数字转k、w结尾 如&#xff1a;10001k 100001w 码 /*** 数字转k,w* param {Number} num * returns String*/ const numberTokw num > {if (num < 1000) return numlet endStr w,numVal 10000;if (num > 999 && num < 10000) {endStr knumVal …

使用python socket搭建Client测试平台

目录 概述 1 背景 2 Client功能实现 2.1 何谓Client 2.2 代码功能介绍 2.3 代码实现 2.3.1 代码介绍 2.3.2 代码内容 3 测试 3.1 PC上创建Server 3.2 同一台PC上运行Client 3.2.1 建立连接 3.2.2 测试数据交互 3.3 Linux 环境下运行Client 3.3.1 建立连接 3.3.…

ElasticSearch实战之项目搜索高亮

文章目录 1. 前情配置2、数据操作2.1 操作API2.2 数据入库 3. 高亮搜索3.1 方法封装3.2 高亮搜索 1. 前情配置 为满足ElasticSearch可在项目中实现搜索高亮&#xff0c;我们需要先做一些前情配置 导入ElasticSearch依赖 <dependency><groupId>org.springframewor…

EPSON晶振应用到汽车电子产品上的型号有哪些?

EPSON品牌应用在汽车电子产品上的晶振.&#xff0c;当然也少不了晶振可能最熟悉的就是32.768K系列和26MHZGPS晶振用的多。 在汽车里每一个部件都应有的不一样,甚至多次使用到同一尺寸,不同频率的晶振.爱普生品牌晶振型号就有几百种,很容易混淆,要想记住汽车里所应用到的不是件…

B树(B-tree)

B树(B-tree) B树(B-tree)是一种自平衡的多路查找树&#xff0c;主要用于磁盘或其他直接存取的辅助存储设备 B树能够保持数据有序&#xff0c;并允许在对数时间内完成查找、插入及删除等操作 这种数据结构常被应用在数据库和文件系统的实现上 B树的特点包括&#xff1a; B树为…

学习空间转换-3D转换

1.什么是空间转换&#xff1f; 使用的是transform属性实现元素在空间内的位移&#xff0c;旋转&#xff0c;缩放等效果。 空间&#xff1a;是从坐标轴角度定义的。x,y,z三条坐标轴构成的一个立体空间&#xff0c;Z轴位置与视线方向相同。 所以空间转换也被叫做3D转换 语法&a…

PICkit 3 v3.10中的 Device Family 识别不到芯片

1&#xff1a;现象描述 在使用 PICkit3烧写hex文件的时候&#xff0c;Device Family只有默认芯片&#xff0c;识别不到当前使用的芯片&#xff0c;导致报错“Device Error - hex file not loaded”&#xff0c;我当前使用的是 PIC16F1826芯片&#xff0c;默认不支持&#xff1…

快速开发部署平台Replit

Replit 是可以快速搭建开发、部署环境的平台。能够快速将原型进行部署&#xff0c;代码编辑器支持协同开发&#xff0c;他的在线编辑器做的不错&#xff0c;相当于云上提供了一个 vscode&#xff0c;代码实时更新。Replit 底层是基于容器技术的&#xff0c;可以安全的隔离多个用…

html、css、QQ音乐移动端静态页面,资源免费分享,可作为参考,提供InsCode在线运行演示

CSDN将我上传的免费资源私自变成VIP专享资源&#xff0c;且作为作者的我不可修改为免费资源&#xff0c;不可删除&#xff0c;寻找客服无果&#xff0c;很愤怒&#xff0c;&#xff08;我发布免费资源就是希望大家能免费一起用、一起学习&#xff09;&#xff0c;接下来继续寻找…

分享|网红阳哥介绍的项目能赚钱吗?具体有哪些项目?

在当今的互联网时代&#xff0c;网红的影响力不容小觑&#xff0c;他们通过分享自己的经验和见解&#xff0c;往往能带动一股潮流或引领一种趋势。其中&#xff0c;网红阳哥以其独到的眼光和丰富的经验&#xff0c;分享了一系列赚钱项目&#xff0c;引起了广泛关注。那么&#…

C语言 逻辑运算符

本文 我们来说 逻辑运算符 有时做出决策需要测试多个条件&#xff0c;C语言提供了用于将简单条件组合成复杂条件的逻辑运算符。 逻辑运算符 如下图 用逻辑运算符连接操作数组成的表达式称为逻辑表达式。 逻辑运算的结果只有0和1 逻辑运算的对象可以是任意数值型&#xff0c;但…

VMWare Ubuntu压缩虚拟磁盘

VMWare中ubuntu会越用越大&#xff0c;直到占满预分配的空间 即使系统里没有那么多东西 命令清理 开机->open Terminal sudo vmware-toolbox-cmd disk shrink /关机-> 编辑虚拟机设置->硬盘->碎片整理&压缩 磁盘应用 开机->disk usage analyzer(应用) …

Springboot+Vue项目-基于Java+MySQL的影城管理系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…