【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.mzph.cn/pingmian/66369.shtml

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

相关文章

【简博士统计学习方法】3. 统计学习方法的三要素

3. 统计学习方法的三要素 3.1 监督学习的三要素 3.1.1 模型 假设空间&#xff08;Hypothesis Space&#xff09;&#xff1a;所有可能的条件概率分布或决策函数&#xff0c;用 F \mathcal{F} F表示。 若定义为决策函数的集合&#xff1a; F { f ∣ Y f ( X ) } \mathcal{F…

【llm/ollama/qwen】在本地部署qwen2.5-coder并在vscode中集成使用代码提示功能

说在前面 操作系统&#xff1a;windows11ollama版本&#xff1a;0.5.4vscode版本&#xff1a;1.96.2continue插件版本&#xff1a;0.8.66 ollama安装 访问官网&#xff0c;点击下载安装即可 默认装在了C盘&#xff0c;比较蛋疼&#xff1b;但是可以指定路径安装&#xff1a;Ol…

PHP零基础入门笔记

表达式&#xff1a;任何有值的东西就是表达式 php可以解析双引号&#xff0c;不可以解析单引号&#xff0c;双引号中引用变量可以输出引用变量的值&#xff0c;但是单引号不可以 1.变量和变量的销毁 unset(变量名) //销毁变量 变量是严格区分大小写的&#xff0c;在php中…

macos安装java8

下载 dmg方式安装 安装 双击pkg运行 输入java -version验证 配置环境变量 cd ~ ls -a输入 ls -a后查看是否已经存在.bash_profile文件&#xff0c;如果已经存在就不需要创建&#xff0c;如果不存在&#xff0c;继续执行下方命令创建文件 touch .bash_profile /usr/l…

自定义校验注解

已有的注解不能满足所有的校验需求,特殊的情况需要自定义校验(自定义校验注解) 1.自定义注解,并在注解上指定校验逻辑 Constraint(validatedBy StateValidation.class) // 指定校验逻辑 package com.example.demo.validation;import jakarta.validation.Constraint; import j…

指代消解:自然语言处理中的核心任务与技术进展

目录 前言1. 指代消解的基本概念与分类1.1 回指与共指 2. 指代消解的技术方法2.1 端到端指代消解2.2 高阶推理模型2.3 基于BERT的模型 3. 事件共指消解&#xff1a;跨文档的挑战与进展3.1 联合模型3.2 语义嵌入模型&#xff08;EPASE&#xff09; 4. 应用场景与前景展望4.1 关键…

Chapter4.1 Coding an LLM architecture

文章目录 4 Implementing a GPT model from Scratch To Generate Text4.1 Coding an LLM architecture 4 Implementing a GPT model from Scratch To Generate Text 本章节包含 编写一个类似于GPT的大型语言模型&#xff08;LLM&#xff09;&#xff0c;这个模型可以被训练来生…

nginx正向代理从安装到使用一网打尽系列(二)使用

一、背景 使用场景大总结&#xff0c;可作为参考手册用 nginx正向代理从安装到使用一网打尽系列&#xff08;一&#xff09;安装 nginx正向代理从安装到使用一网打尽系列&#xff08;二&#xff09;使用 二、使用场景 1、所有内网应用都不能直接访问外网&#xff0c;但需要…

论文解读 | NeurIPS'24 IRCAN:通过识别和重新加权上下文感知神经元来减轻大语言模型生成中的知识冲突...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 点击 阅读原文 观看作者讲解回放&#xff01; 作者简介 史丹&#xff0c;天津大学博士生 内容简介 大语言模型&#xff08;LLM&#xff09;经过海量数据训练后编码了丰富的世界知识。最近的研究表明&#xff0c…

大语言模型训练所需的最低显存,联邦大语言模型训练的传输优化技术

联邦大语言模型训练的传输优化技术 目录 联邦大语言模型训练的传输优化技术大语言模型训练所需的最低显存大语言模型训练所需的最低显存 基于模型微调、压缩和分布式并行处理的方法,介绍了相关开源模型及技术应用 核心创新点 多维度优化策略:综合运用基于模型微调、模型压缩和…

(CICD)自动化构建打包、部署(Jenkins + maven+ gitlab+tomcat)

一、平滑发布与灰度发布 **什么叫平滑&#xff1a;**在发布的过程中不影响用户的使用&#xff0c;系统不会因发布而暂停对外服务&#xff0c;不会造成用户短暂性无法访问&#xff1b; **什么叫灰度&#xff1a;**发布后让部分用户使用新版本&#xff0c;其它用户使用旧版本&am…

Python 科学计算

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/literature?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;…

【pytorch】注意力机制-1

1 注意力提示 1.1 自主性的与非自主性的注意力提示 非自主性提示&#xff1a; 可以简单地使用参数化的全连接层&#xff0c;甚至是非参数化的最大汇聚层或平均汇聚层。 自主性提示 注意力机制与全连接层或汇聚层区别开来。在注意力机制的背景下&#xff0c;自主性提示被称为查…

Qt窗口获取Tftpd32_svc服务下载信息

前言 一个由Qt开发的Windows小工具需要布置Tftp协议服务端来支持设备下载数据&#xff0c;并显示下载列表&#xff08;进度、下载源等&#xff09;。 考虑开发方便&#xff0c;优先使用了Qtftp方案&#xff0c;经测试发现&#xff0c;不够稳定&#xff0c;会有下载超时的情况&a…

<OS 有关> DOS 批处理命令文件,用于创建 python 虚拟机,并进入虚拟机状态执行后继命令 判断虚拟机是否存在,在批处理文件中自定义 虚拟机名字

前言&#xff1a; 经常要敲重复的命令&#xff1a; python -m venv venv.\venv\Scripts\activate.bat (虽然能按 Tab 省几下&#xff0c;多了也烦恼&#xff0c;后来写了四行脚本 start.bat) DOS批处理 create_venv_start.bat 功能&#xff1a; 批处理显示支持中文在 creat…

【算法刷题】leetcode hot 100 哈希篇

文章目录 1. 两数之和49. 字母异位词分组128. 最长连续序列总结 1. 两数之和 leetcode&#xff1a;https://leetcode.cn/problems/two-sum/description/?envTypestudy-plan-v2&envIdtop-100-liked暴力解决&#xff1a; public int[] twoSum(int[] nums, int target) {for …

iOS 逆向学习 - iOS Architecture Cocoa Touch Layer

iOS 逆向学习 - iOS Architecture Cocoa Touch Layer 一、Cocoa Touch Layer 简介二、Cocoa Touch Layer 的核心功能1. UIKit2. Event Handling&#xff08;事件处理&#xff09;3. Multitasking&#xff08;多任务处理&#xff09;4. Push Notifications&#xff08;推送通知&…

网站常用功能模块-鉴权

一&#xff1a;JWT是什么&#xff1f; 常用鉴权方式有很多种&#xff0c;今天主要介绍基于token的鉴权方式JWT&#xff08;Json JSON Web Token&#xff09;。因为这种方式实现起来方便快捷。整体实现逻辑如下 第一次登陆时&#xff0c;前端携带账号和密码请求登录接口。服务…

stm32的掉电检测机制——PVD

有时在一些应用中&#xff0c;我们需要检测系统是否掉电了&#xff0c;或者要在掉电的瞬间需要做一些处理。 STM32内部自带PVD功能&#xff0c;用于对MCU供电电压VDD进行监控。 STM32就有这样的掉电检测机制——PVD(Programmable Voltage Detecter)&#xff0c;即可编程电压检…

Flink系列知识讲解之:深入了解 Flink 的网络协议栈

Flink系列知识之&#xff1a;深入了解 Flink 的网络协议栈 Flink 的网络协议栈是组成 flink-runtime 模块的核心组件之一&#xff0c;也是每个 Flink 任务的核心。它连接着来自所有任务管理器的各个工作单元&#xff08;子任务&#xff09;。这是流数据流过的地方&#xff0c;…