【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:表情包来自网络,侵删🌹

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

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

相关文章

海外云服务器能用来做什么?

海外云服务器不仅服务种类繁多&#xff0c;而且能满足多行业的需求&#xff0c;方便了越来越多的企业与个人。本文将探讨海外云服务器的核心服务及其适用领域&#xff0c;帮助企业更好地了解这一技术资源。 云存储&#xff1a;安全高效的数据管理 海外云服务器为用户提供了稳定…

计算机毕业设计Python+CNN卷积神经网络高考推荐系统 高考分数线预测 高考爬虫 协同过滤推荐算法 Vue.js Django Hadoop 大数据毕设

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

基于物联网的冻保鲜运输智能控制系统

基于物联网的冻保鲜运输智能控制系统设计文档 1. 项目开发背景 随着全球化贸易的发展&#xff0c;冷链物流在现代运输行业中扮演着日益重要的角色。尤其是冻品、食品、药品等对运输环境有着严格要求的货物&#xff0c;其运输过程中温度、湿度等环境参数必须严格控制&#xff…

资源分享:gpts、kaggle、paperswithcode

gpts 似乎是gpt agent集合&#xff0c;专注于不同细分方向的ai助手。 kaggle 专注于AI相关的培训、竞赛、数据集、大模型。 paperswithcode 简单直接&#xff0c;内容如同网站地址&#xff0c;直接提供优秀代码和配套的论文&#xff0c;似乎还有数据集。

谷歌浏览器的书签同步功能详解

谷歌浏览器作为全球最受欢迎的网络浏览器之一&#xff0c;提供了众多强大的功能来提升用户的上网体验。其中&#xff0c;书签同步功能允许用户在不同设备之间无缝地同步浏览器数据&#xff0c;如书签、历史记录、密码等。本文将详细解析谷歌浏览器的书签同步功能&#xff0c;教…

pip error: microsoft visual c++ 14.0 or greater is required

报错原因&#xff1a;软件包作者发布的是为编译的*.tar.gz包&#xff0c;我们安装的时候需要调用系统C编译器来进行编译安装&#xff0c;如果系统没有安装编译器或者编译器版本不对就会报这个错误。 解决方式一&#xff1a;安装编译器&#xff0c;但不需要安装完整的visual c …

Windows提示msvcp120.dll丢失怎么解决?Windows文件丢失的4种解决方法,教你修复msvcp120.dll文件

Windows提示msvcp120.dll丢失&#xff1f;别担心&#xff0c;这里有4种解决方法&#xff01; 作为软件开发领域的一名从业者&#xff0c;我经常遇到用户反馈关于Windows系统报错的问题&#xff0c;其中“msvcp120.dll丢失”是一个较为常见的错误。今天&#xff0c;我将为大家科…

ESP32-C3 AT WiFi AP 启 TCP Server 被动接收模式 + BLE 共存

TCP 被动接收模式&#xff0c;每次发的数据会先存到缓冲区&#xff0c;参见&#xff1a;ATCIPRECVTYPE 指令说明。 即每包数据不会实时报告 IPD 接收情况&#xff0c;如果需要查询缓冲区的数据&#xff0c;先用 ATCIPRECVLEN? 指令查询被动接收模式下套接字数据的长度 。获取…

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

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

pytest测试用例管理框架特点及常见语法和用法分享

一、pytest及其特点 1. 什么是pytest pytest 是一个功能强大且灵活的 Python 测试框架&#xff0c;也是目前最流行的测试框架&#xff0c;可以让我们很方便的编写和管理自动化测试用例&#xff0c;并提供丰富的插件来满足单元测试、集成测试、性能测试等各种测试需求。 2. p…

现代密码学期末重点(备考ing)

现代密码学期末重点&#xff0c;个人备考笔记哦 密码学概念四种密码学攻击方法什么是公钥密码&#xff1f;什么是对称密码&#xff1f;什么是无条件密码&#xff1f; 中国剩余定理&#xff08;必考&#xff09;什么是原根什么是阶 经典密码学密码体制什么是列置换&#xff1f; …

HarmonyOS:@Builder装饰器:自定义构建函数

一、前言 ArkUI提供了一种轻量的UI元素复用机制Builder&#xff0c;其内部UI结构固定&#xff0c;仅与使用方进行数据传递&#xff0c;开发者可以将重复使用的UI元素抽象成一个方法&#xff0c;在build方法里调用。 为了简化语言&#xff0c;我们将Builder装饰的函数也称为“自…

VISRAG论文介绍:一种直接的视觉RAG

今天给大家介绍一篇论文&#xff0c;VISRAG: VISION-BASED RETRIEVAL-AUGMENTED GENERATION ON MULTI-MODALITY DOCUMENTS [pdf]&#xff0c;一种直接的视觉RAG。 Source&#xff08;来源&#xff09;:ICLR2025 Summary: &#xff08;文献方向归纳 &#xff09;多模态RAG Mot…

在 .Net 8.0 中使用 AJAX 在 ASP.NET Core MVC 中上传文件

上传文件是现代 Web 应用程序中的常见要求。在 ASP.NET Core MVC 中&#xff0c;高效处理文件上传可以提高应用程序的可用性和性能。在本文中&#xff0c;我们将探讨如何使用 AJAX 在 ASP.NET Core MVC 应用程序中实现文件上传&#xff0c;通过允许文件上传而无需刷新整个页面&…

简单的spring boot tomcat版本升级

简单的spring boot tomcat版本升级 1. 需求 我们使用的springboot版本为2.3.8.RELEASE&#xff0c;对应的tomcat版本为9.0.41&#xff0c;公司tomcat对应版本发现攻击者可发送不完整的POST请求触发错误响应&#xff0c;从而可能导致获取其他用户先前请求的数据&#xff0c;造…

linux系统(ubuntu,uos等)连接鸿蒙next(mate60)设备

以前在linux上是用adb连接&#xff0c;现在升级 到了鸿蒙next&#xff0c;adb就不好用了。得用Hdc来了&#xff0c;在windows上安装了hisuit用的好好的&#xff0c;但是到了linux(ubuntu2204)下载安装了 下载中心 | 华为开发者联盟-HarmonyOS开发者官网&#xff0c;共建鸿蒙生…

C++:位与运算符

& 一&#xff0c;位与运算符的运算规则 有0则0。 二&#xff0c;判断奇偶性 %&#xff1a;优先级高&#xff0c;效率低 &&#xff1a;优先级低&#xff0c;效率高 数与1的位与运算结果为1则为奇数&#xff0c;结果为0则为偶数 三&#xff0c;获取一个数二进制的后…

(已开源-AAAI25) RCTrans:雷达相机融合3D目标检测模型

在雷达相机融合三维目标检测中&#xff0c;雷达点云稀疏、噪声较大&#xff0c;在相机雷达融合过程中提出了很多挑战。为了解决这个问题&#xff0c;我们引入了一种新的基于query的检测方法 Radar-Camera Transformer (RCTrans)。具体来说&#xff1a; 首先设计了一个雷达稠密…

如何利用PHP爬虫按关键字搜索淘宝商品

在当今的电商时代&#xff0c;获取淘宝商品信息对于市场研究、价格监控和竞争分析等方面具有重要意义。手动搜索和整理大量商品信息不仅耗时耗力&#xff0c;而且容易出错。幸运的是&#xff0c;PHP爬虫技术为我们提供了一种高效、自动化的方式来按关键字搜索淘宝商品。本文将详…