析构函数详解

目录

  • 析构函数
    • 概念
    • 特性
    • 对象的销毁顺序

感谢各位大佬对我的支持,如果我的文章对你有用,欢迎点击以下链接
🐒🐒🐒 个人主页
🥸🥸🥸 C语言
🐿️🐿️🐿️ C语言例题
🐣🐣🐣 python
🐓🐓🐓 数据结构C语言
🐔🐔🐔 C++
🐿️🐿️🐿️ 文章链接目录

析构函数

概念

通过上一篇文章我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?

析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。

而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作(有点像数据结构中的Destroy函数,作用就是清理链表 树 或堆上的空间清理,如果不清理会出现内存泄漏的情况)

注意对象空间的开辟和销毁不需要我们去解决,这些都是由系统去完成的(全局 对象 静态都是系统自己去解决),而像堆上的空间就需要我们去完成了,比如malloc开辟空间的时候需要我们去完成,在最后释放的时候也是需要我们自己去free掉空间

特性

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

  1. 析构函数名是在类名前加上字符 ~
  2. 无参数无返回值类型。
    具体结构如下:
class Date
{~Date(){;}
};

类名前的~在C语言中表示按位与取反,这里的取反有完全相反的意思,所以 ~放在析构函数这里就是想说明析构函数的作用和构造函数是完全不同的

特别注意析构函数是没有参数的,而构造函数是有参数的,因为构造函数要构造,传参可以初始化,而析构函数完全没必要传参,所以就没有参数

  1. 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载
class Date
{
public:Date(){_year = 1;}~Date(){cout << "~Date()" << endl;}
private:int _year;
};
int main()
{Date d1;return 0;
}

在这里插入图片描述

  1. 对象生命周期结束时,C++编译系统系统自动调用析构函数。

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

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

由于析构函数和构造函数都是特殊的类,所以都是有this指针的

class Date
{
public:Date(){_year = 1;}~Date(){cout << this << endl;cout << "~Date()" << endl;}void Print(){cout << this << endl;cout << "Print()" << endl;}
private:int _year;
};
void func()
{Date d2;
}
int main()
{func();Date d1;d1.Print();return 0;
}

通过调试我们可以看到,d1和d2的地址以this指针的方式传给函数,d1和d2在生命周期结束时会调用析构函数,而析构函数里面是打印this指针
在这里插入图片描述
我们来看看下面的代码来具体理解析构函数

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 s;s.Push(1);s.Push(2);
}

在C语言中当没有调用Destroy函数会发生内存泄漏,具体过程如下

main函数会在栈上开辟一块空间,这块空间中也包含Stack s的指针DataType* _array(只是这个指针在mian函数开辟的空间里)
在这里插入图片描述
DataType* _array中_arrray的作用是保存Stack s开辟空间的地址
在main函数执行完后,会将main函数在栈上开辟的空间都销毁,其中就包括了指针_array
由于_array是保存着Stack s开辟空间的地址,最终会因为指针_array被销毁,导致找不到Stack s开辟出的空间

在这里插入图片描述
所以没调用Destroy函数发生的后果是很严重的,并且我们经常会忘记调用Destroy函数,为了解决这个问题才有了析构函数,因为析构函数自动调用,并且编译器可以自动生成析构函数,这对我们来说是非常方便的

但是需要注意的是默认生成的析构函数和默认生成的构造函数类似,对内置类型不做处理,自定义类型的成员会去调用他的析构函数

对象的销毁顺序

生命周期对于现在学到的来说有两种,一种是局部(存在一些函数中,因为调用函数会开辟栈帧,所以函数结束后栈帧也会被销毁,函数中的局部变量也就销毁了),另一种是静态或者全局的(存在静态区里,在mian函数结束后就会销毁)

而对象生命周期结束时,C++编译系统系统自动调用析构函数,那如果有多个对象生命周期同时结束,系统会优先给谁调用析构函数

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(1);Date d2(2);
}

这段代码中我们只定义了一个成员变量_year,其他的_month以及_day都只是声明,不占用内存空间,mian函数中Date d1(1),Date d2(2)是对_year进行初始化,在函数结束后两个对象的生命周期都会结束,而销毁的顺序如图
在这里插入图片描述
这个调用的顺序像栈中的后进先出,Date d1先入栈,所以最后调用析构函数,事实上对象确实存储在栈上的,因为类其实是一个函数,在函数调用时会建立栈帧,所以空间存储在栈上

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
int main()
{Date d1(1);Date d2(2);static Date d3(3);
}

那如果让Date d3加上一个static去修饰结果会怎么样
在这里插入图片描述
加上static修饰后Date d3的存储区域就发生变化了,d3存储在一个单独的静态区中,虽然d3是一个局部变量,但是他的生命周期在经过static修饰后变成全局,所以d3会在main函数结束后销毁,而在main函数结束前会将里面的d1和d2等局部变量先销毁掉,所以d3排在最后

我们再来看看下面的代码,这段代码中定义了一个函数func,将类的对象定义在函数中,其中d4是被static修饰的,而d3没有被修饰,然后在main函数中调用func

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
void func()
{Date d3(3);static Date d4(4);
}
int main()
{Date d1(1);Date d2(2);func();
}

销毁顺序是3 2 1 4,具体原因还是因为func也是一个函数,空间开辟在栈上的,满足后进先出原则,所以先销毁对象d3(对于d3为什么是最先销毁,可能是因为他在函数func中,算是一个局部中的局部吧),然后又是d2 d1,d4因为被static修饰,所以最后销毁
在这里插入图片描述
我们再在main函数外定义一个对象d5又会怎么样

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
void func()
{Date d3(3);static Date d4(4);
}
Date d5(5);
int main()
{Date d1(1);Date d2(2);func();
}

结果是3 2 1 4 5
在这里插入图片描述
d5虽然没有被static修饰,但是他定义在main函数外的,所以他自己就是一个全局变量,但是这里的全局变量有两个,一个是d4,一个是d5,他们的销毁顺序是否也和自己的位置有关呢?

我们将d5的定义移到func函数上边,发现没有变化,所以推测可能是因为d4是在func函数中,所以相对于d5来讲,d4的声明周期是局部的
在这里插入图片描述

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
static Date d6(6);
Date d5(5);
void func()
{Date d3(3);static Date d4(4);
}
int main()
{func();Date d1(1);Date d2(2);

我们再在main函数外定义一个d6,用static修饰他的顺序又会怎么样
在这里插入图片描述
当d6和d5交换顺序后,发现销毁的顺序变化了,所以我们得出结论,全局的销毁顺序和局部的销毁顺序也是一样的,当d5后入栈时,d5就先销毁,而static修饰全局变量d6,并不会改变d6的销毁顺序
在这里插入图片描述
在这里插入图片描述
如果在多个函数func中定义类的顺序会怎么样

class Date
{
public:Date(int year){_year = year;
}~Date(){cout << "~Date()" << _year<<endl;}
private:int _year;int _month;int _day;
};
static Date d6(6);
Date d5(5);
void func2()
{Date d7(7);static Date d8(8);
}
void func1()
{Date d3(3);static Date d4(4);
}int main()
{Date d1(1);Date d2(2);func1();func2();
}

在这里插入图片描述
这里说下我的想法,func1和func2因为都是在栈上开的空间,所以他们的销毁的顺序满足后进先出,具体判断谁先销毁的方法就是看谁最先被调用,也就是在main函数中去看func1是否比func2先调用,如果先比func2调用,那就说明func1先开辟空间,所以func1要比func2后销毁
在这里插入图片描述
最终顺序总结如下
局部对象(后定义先析构)->局部静态->全局对象(后定义先析构)

类中没有显示定义析构函数,系统则会自动生成默认的析构函数,那这个析构函数是否和构造函数一样基本上什么事都不做呢?
由于自定义类型的尽头是内置类型,对应类而言如果类中没有申请资源时,析构函数可以不写(因为不写不会有影响),有资源申请时,一定要写,否则会造成资源泄漏

为什么析构不可以自己去处理内置类型呢?
因为内置类型中有指针等许多不能随便处理的类型,假如指针指向了一块空间,如果析构函数可以处理内置类型的话,有可能会直接把指针指向的空间给销毁了,这样指针就变成了野指针

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

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

相关文章

yolov8实战之 .pt 转. tensorRT

1 yolo 训练 1.1修改自己的数据集合 我是有3个类别&#xff0c;差不多这么些数据 1.2 训练 from ultralytics import YOLO # Load a model model YOLO("yolov8m.yaml") # build a new model from scratch #model YOLO(E:/pythonCode/pythonProject1/runs/detec…

风电功率预测 | 基于PSO-BP神经网络实现风电功率预测(附matlab完整源码)

风电功率预测 风电功率预测完整代码风电功率预测 基于粒子群优化算法(Particle Swarm Optimization, PSO)的BP神经网络是一种常见的方法,用于实现风电功率预测。下面是一个基于PSO-BP神经网络实现风电功率预测的一般步骤: 数据准备:收集与风电场发电功率相关的数据,包括…

农林科学SCI期刊,IF=6+,影响力高,对国人非常友好!

一、期刊名称 Crop Journal 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;农林科学 影响因子&#xff1a;6.6 中科院分区&#xff1a;1区 出版方式&#xff1a;开放出版 版面费&#xff1a;$900 三、期刊征稿范围 《作物杂志》是一份双月刊、国际、同…

PHP使用Browsershot进行网页截图

Browsershot是什么 Spatie Browsershot 是一个开源PHP库&#xff0c;它允许开发者在PHP应用程序中生成网页的截图。 这个库特别适用于Laravel框架&#xff0c;但也可以在其他 PHP 应用程序中使用。 主要特点 无头浏览器截图&#xff1a;使用无头版本的 Chrome 或 Chromium 浏…

10W 3KVAC隔离 宽电压输入 AC/DC 电源模块 ——TP10AF系列

TP10AF系列输出功率为10W&#xff0c;具有可靠性高、更小的体积、性价比高等特点&#xff0c;广泛用于工控和电力仪器、仪表、智能家居等相关行业。

SMB攻击利用之-mimikatz上传/下载流量数据包逆向分析

SMB协议作为windows环境下最为常见的一种协议,在历史上出现过无数的通过SMB协议进行网络攻击利用的案例,包括针对SMB协议本身以及通过SMB协议实施网络攻击。 本文将介绍一种通过SMB协议的常见利用方式,即向远程主机传输mimikatz,作为我的专栏《SMB攻击流量数据包分析》中的…

前端学习第一课

AJAX 事先说明&#xff0c;这只是记录&#xff0c;并不是从零到一的教学内容&#xff0c;如果想要学习的话&#xff0c;可以跳过本文章了 ok&#xff0c;转回正题&#xff0c;正如上面所说&#xff0c;这只是记录。其实我是有一定的前端基础的&#xff0c;也做过涉及相关的开发…

【工具】macOS、window11访问limux共享目录\共享磁盘,samba服务安装使用

一、samba服务安装 Samba是一个免费的开源软件实现&#xff0c;使得非Windows操作系统能够与Windows系统进行文件和打印服务共享。它实现了SMB/CIFS协议&#xff0c;并且能够在Linux、Unix、BSD等多种系统上运行。 安装 samba&#xff1a; sudo yum install samba配置 samba…

【介绍下Python多线程,什么是Python多线程】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

【气象常用】时间序列的线性拟合

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;下载Hadley Centre observations datasets的HadSST数据 可参考【气象常用】时间序列图-CSDN博客 2. 数据处理&#xff1a;计算线性拟合 3. 图像绘制&#xff1a;绘制折线及拟合线&#xff0c;并添加文本 …

其他自动化工程师都在偷偷学习AI技术,你再不学就落后了!一篇文章教会你使用AI!

其他自动化工程师都在偷偷学习AI技术&#xff0c;你再不学就落后了&#xff01;一篇文章教会你使用AI&#xff01; 哈喽&#xff0c;大家好&#xff0c;我是小叔。了解小叔的朋友都清楚&#xff0c;我从来都不是标题党&#xff0c;我只会用美女图片来吸引你们&#x1f602;&am…

python 六句话让电脑告诉你,刚才插入的串口编号

六句话让电脑告诉你&#xff0c;我的串口号 第一步&#xff0c;安装python 编译器以及pyserial 模块第二步&#xff0c;写入代码 import serial.tools.list_ports usart_list list(serial.tools.list_ports.comports()) input("Please insert your serial port:")…

midjourney里有哪些常用参数?

一、stylize参数 Midjourney 经过训练&#xff0c;可以生成更加具有艺术色彩、构图和形式的图像。 --stylize或参数--s影响该训练的应用程度。 低风格化值生成的图像与提示词非常匹配&#xff0c;但艺术性较差。数值越高艺术性更好&#xff0c;但是和描述词相关性更差&#…

【python量化交易】—— 双均线择时策略 - Qteasy自定义交易策略【附源码】

使用qteasy自定义并回测双均线交易策略 使用qteasy自定义并回测一个双均线择时策略策略思想导入qteasy模块创建一个新的策略回测交易策略&#xff0c;查看结果 使用qteasy自定义并回测一个双均线择时策略 我们今天使用qteasy来回测一个双均线择时交易策略&#xff0c;qteasy是…

Spring初学入门(跟学笔记)

一、Spring概述 Spring是一款主流的Java EE轻量级开源框架。 Spring的核心模块&#xff1a;IoC&#xff08;控制反转&#xff0c;指把创建对象过程交给Spring管理 &#xff09;、AOP&#xff08;面向切面编程&#xff0c;在不修改源代码的基础上增强代码功能&#xff09; 二、…

AI大语言模型在公共服务中的应用实例

随着计算机技术的飞速发展&#xff0c;人工智能已经成为了当今科技领域的热门话题。从早期的图灵测试到现在的深度学习和神经网络&#xff0c;人工智能已经取得了令人瞩目的成就。特别是近年来&#xff0c;大数据、云计算、高性能计算等技术的发展为人工智能的研究提供了更加广…

多客陪玩系统源码,线上游戏开黑陪玩,线下预约家政服务,语音陪聊,陪玩成品搭建,源码交付,支持二开,陪玩系统开发

游戏陪玩系统主要的优势就是&#xff0c;只要有手游和网游不断推出&#xff0c;就有钱可赚。为什么呢?因为电竞行业正处于上升发展阶段&#xff0c;而且玩游戏对于现代人来说是很好的一种解压方式&#xff0c;所以在市场和用户需求方面都是有保证的。再加上现代人的社交压力越…

使用 5 种有用的方法将音乐从 iTunes 传输到安卓手机

有许多在线音乐应用程序可供您选择&#xff0c;但如果您想在 安卓手机上欣赏 iTunes 音乐&#xff0c;您需要了解步骤。今天的主题是如何将音乐从 iTunes 传输到 安卓设备上。虽然没有适用于 Android 的 iTunes&#xff0c;但您可以在此处获取 5 种有用的方法将 iTunes 音乐传输…

手机自养号测评系统:专业应对电商平台风控,提升账号稳定性

用手机做自养号测评它具备无限生成不同真实手机底层环境的能力&#xff0c;每个环境都相当于一台全新的手机设备。通过先进的底层屏蔽技术&#xff0c;我们成功让亚马逊等平台仅能检测到我们预设的参数&#xff0c;如手机型号、内存、lMEI、序列号、MAC地址以及运营商信息等。每…

Stable Diffusion超详细教程!本地部署 Stable Diffusion

前言 目前市面上比较权威&#xff0c;并能用于工作中的AI绘画软件其实就两款&#xff1a; Midjourney&#xff08;MJ&#xff09;Stable-Diffusion&#xff08;SD&#xff09; MJ需要付费使用&#xff0c;而SD开源免费&#xff0c;但是上手难度和学习成本略大&#xff0c;并…