析构函数详解

news2025/8/7 6:02:51

析构函数

  • 1.概念与特性
  • 2.工作原理
  • 4.析构的顺序

如果一个类中什么成员都没有,那么该类简称为空类。而空类中其实并不是真的什么都没有,任何类在什么都不写时,编译器会自动生成以下6个默认成员函数。

  • 构造函数:主要完成初始化工作
  • 析构函数:主要完成资源的清理工作
  • 拷贝构造函数:主要用于使用同类对象初始化创建对象
  • 赋值运算符重载:主要是把一个对象赋值给另一个对象
  • 普通对象取地址重载
  • const对象取地址重载(取地址重载很少会自己实现)

本篇文章,我们来学习析构函数。

1.概念与特性

概念:

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

析构函数:析构函数不是完成对对象本身的销毁。在对象销毁的过程中,对象中的局部成员变量会由操作系统自动回收,而由我们动态申请的内存则不会由操作系统自动回收,编译器会调用析构函数来清理对象中所申请的资源。如果对象中没有动态申请的资源,那么析构函数实际上没有需要完成的任务,这种情况下,只需要让编译器生成一个什么都不做的隐式析构函数即可。

特性:

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

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

class Stack
{
public:
	Stack(size_t capacity = 3)
	{
		DataType* _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;
};
void TestStack()
{
	Stack s;
	s.Push(1);
	s.Push(2);
}

2.工作原理

析构函数在对象消亡时被调用,以清理对象所申请的资源,那具体它在何时被调用呢?

析构函数主要在以下4种情况下会被调用:

对象生命周期结束,此时会自动调用析构函数。

#include<iostream>
using namespace std;
class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
};

int main() {
    Person person;
    return 0;
}

运行结果如下图所示:

在这里插入图片描述

该运行结果说明,在对象的生命周期结束后,会自动调用对象的析构函数。

调用delete函数释放new出来的空间:由于是new出来的对象是在堆上分配空间的,即使离开了作用域,其依然存在,我们必须主动delete来释放new出来的在堆上的空间,delete函数会调用析构函数来清理对象申请的资源,否则离开了作用域后,指向该对象空间的指针就会消失,我们失去了对这片空间的控制权,别人也无法使用这片空间,这就会造成内存泄漏。

#include <iostream>
using namespace std;
class Person {
public:
    Person() {
        cout <<  "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
};

int main() {
    Person* person = new  Person();
    delete person;
    return 0;
}

在这里插入图片描述

成员关系:对象car是对象person的成员,person的析构函数被调用时,对象car的析构函数也被调用。

#include <iostream>
using namespace std;
class Car {
public:
    Car() {
        cout << "调用了Car的构造函数" << endl;
    }
    ~Car() {
        cout << "调用了Car的析构函数" << endl;
    }
private:
    int name;
};
class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;
    Car car;
};


int main() {
    Person person;
    return 0;
}

在这里插入图片描述

继承关系:当Person是Student的父类,调用Student的析构函数,会调用Person的析构函数。

#include <iostream>
using namespace std;

class Person {
public:
    Person() {
        cout << "调用了Person的构造函数" << endl;
    }
    ~Person() {
        cout << "调用了Person的析构函数" << endl;
    }
private:
    int name;

};
class Student :public Person {
public:
    Student() {
        cout << "调用了Student的构造函数" << endl;
    }
    ~Student() {
        cout << "调用了Student的析构函数" << endl;
    }
private:
    int name;
    string no;
};

int main() {
    Student student;
    return 0;
}

在这里插入图片描述

默认析构函数是怎么工作的呢?

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;
}

程序运行结束后输出:~Time(),在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?

默认析构函数,对于内置类型成员不做处理,对于自定义类型成员会去调用它的析构函数。

总结: 默认构造函数对内置类型不做处理,对自定义类型会调用它的默认构造函数,默认析构函数对内置类型不做处理,对自定义类型会调用它的析构函数。

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

4.析构的顺序

因为局部变量是存储在栈中的,因此最后创建的对象将最先被删除,最先创建的对象最后被删除,即局部对象,构造函数和析构函数是相反的顺序,static改变对象的生存作用域,会放在局部对象之后,全局对象之前进行析构。

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

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

相关文章

内网渗透神器CobaltStrike之配置与基础操作(一)

CobaltStrike简介 Cobalt Strike: C/S架构的商业渗透软件&#xff0c;适合多人进行团队协作&#xff0c;可模拟APT做模拟对抗&#xff0c;进行内网渗透。 Cobalt Strike 一款GUI的框架式渗透工具&#xff0c;集成了端口转发、服务扫描&#xff0c;自动化溢出&#xff0c;多模…

megahit源码迁移解析

megahit源码迁移大作业 在进行megahit源码迁移前需要清理自己的实验环境 1、链接鲲鹏服务器 2、进入源码存放地址/opt/portadv/portadmin/sourcecode 环境准备&#xff0c;清理之前实验环境后下载本次实验所需的源码 之前存在的其他文件&#xff0c;删除code 登录代码迁移工…

Python常见操作的时间复杂度

Python常见操作的时间复杂度 本文整理了Python中常见数据结构操作的时间复杂度&#xff0c;旨在帮助大家了解Python操作的性能&#xff0c;协助运行更快的代码。 文章目录标注方法List操作Set操作Deque操作标注方法 程序时间复杂度一般用"大O表示法&#xff08;Big-O no…

windows11系统WSL2安装ubuntu20.04桌面

文章目录1. MobaXterm安装2.WSL安装xfce desktop3. 连接桌面参考链接1. MobaXterm安装 这个比较简单&#xff0c;没介绍 2.WSL安装xfce desktop 安装命令 sudo apt-get install xfce4-terminal sudo apt-get install xfce4安装完之后需要稍微配置一下&#xff1a; export …

【并发编程六】c++进程通信——信号量(semaphore)

【并发编程六】c进程通信——信号量&#xff08;semaphore&#xff09;一、概述二、信号量三、原理四、过程1、进程A过程2、进程B过程五、demo1、进程A2、进程B六、输出七、windows api介绍1. 创建信号量 CreateSemaphore()2. 打开信号量 OpenSemaphore()3. 等待 WaitForSingle…

力扣LeatCode算法题第三题-无重复字符的最长子串

要求&#xff1a; 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长子串 的长度。 我一开始采用的第一种方法是使用hashmap去比对大小&#xff0c;在idea上可以跑通程序&#xff0c;但在leatcode的编译器中&#xff0c;无法通过字符串s"" 和s"…

苹果推送和开发证书更新

1.背景 推送证书&#xff08;生产Apple Push Services和开发APNs Development iOS&#xff09;的有效期都是一年&#xff0c;将要过期的时候&#xff0c;苹果官方会发邮件提醒。 2.csr和推送证书更新 打开mac电脑&#xff0c;找到启动台-->其他&#xff0c;打开钥匙串访问。…

uni-app入门:WXML数据绑定

1.简单数据绑定 2.组件属性数据绑定 3.运算绑定 3.1三元运算符 3.2算数运算 3.3字符串拼接运算 3.4逻辑判断运算 正文 WXML全称&#xff1a;wexin markup language,微信标签语言&#xff0c;可以理解为web中的html&#xff0c;…

JavaEE之HTTP协议 Ⅱ

文章目录一、认识请求"报头"(header)1.HOST2.Content-Length3.Content-Type4.User-Agent (简称 UA)5.Referer4.Cookie二、HTTP响应详解1. 认识"状态码"(status code)2. Content-Type3. 如何构造HTTP请求总结一、认识请求"报头"(header) 这里的键…

php资源列表|开发者应知晓

PHP PSR 代码标准 官网: https://www.php-fig.org原文: https://github.com/php-fig/fi...中文: https://psr.phphub.orgPHP资源列表PHP设计模式PHP知识技能树PHP资源站 PHP WeeklyCodeVisuallyPHP LeagueKnpLabs开发项目 FastAdmin - 基于 ThinkPHP5 + Bootstrap 的极速后台开…

ubuntu搭建Elasticsearch过程与问题

目录 一.下载Elasticsearh 二.解压 创建需要的文件目录 三.修改配置文件 四.遇到的问题 1.root账号启动问题 2.创建文件不授权切换到非root用户test的时候报错 3.启动最后还是报错 4.浏览器请求http://localhost:9200 报错&#xff1a;received plaintext http traffic…

【python】算法与数据结构作业记录分析

目录 算法与数据结构实验题 9.21 朋友圈 ★实验任务 ★数据输入 ★数据输出 输入示例 输出示例 代码实现 效果展示 算法与数据结构实验题 9.24 水杯 ★实验任务 ★数据输入 ★数据输出 输入示例 输出示例 代码实现 效果展示 算法与数据结构实验题 9.21 朋友圈 …

实验(四):LCD1602显示实验

一、实验目的与任务 实验目的&#xff1a; 1. 掌握LCD1602显示控制方法&#xff1b; 2. 掌握利用Proteus进行单片机控制系统的仿真及调试方法。 3. 掌握单片机开发板的使用。 任务&#xff1a; 1.根据要求编写程序&#xff0c;并写出原理性注释&#xff1b; 2. 将检查程序运行的…

Java入门项目——读书管理系统

Java简单实现读书管理系统一、前言二、思路及整体框架三、代码展示1.有关读书包&#xff08;Book&#xff09;2.有关用户包3.有关操作书的包一、前言 相信有很多小伙伴学习完了【JavaSE】基础语法&#xff0c;想知道自己到底学的怎么样&#xff0c;或则学完不知道这么把知识点…

JavaFX之Scene Builder的使用(开发一款GUI小工具原来这么简单)

文章目录一、前言二、JavaFX与Scene Builder下载三、Scene Builder的使用四、详细教学&#xff08;示例&#xff09;4.1 环境配置4.2 创建fxml文件以及Controller类文件4.3 自定义界面4.4 运行我们的程序五、拓展总结博主个人社区&#xff1a;开发与算法学习社区 博主个人主页&…

创建.gitignore文件并使用

创建 .gitignore文件 第一种方式 在项目根目录下直接创建一个文件&#xff0c;后缀改成 .gitignore 即可。 第二种方式 用git创建&#xff0c;到根目录下&#xff0c;执行 touch .gitignore&#xff0c;即可看见目录下已经出现了该忽略文件。 添加忽略规则 # 忽略所有以 …

httpOnly对于抵御Session劫持的个人小结

Ⅰ 什么是http only?起到什么防护作用&#xff1f; cookie中设置了HttpOnly属性&#xff0c;那么通过js脚本将无法读取到cookie信息&#xff0c;主要防护的攻击手段&#xff1a;XSS不能通过document对象直接获取cookie Ⅱ 怎么绕过http only的防护&#xff08;三种&#xff…

5、CSS——三种样式和样式优先级、CSS中颜色设置方式、标签选择器和基本选择器的优先级

目录 一、行内样式 二、内部样式 三、外部样式 1、创建外部样式步骤 2、引入外部样式的两种方式 2.1 第一种 2.2 第二种 3、style标签内的注释符号 四、样式优先级 五、CSS中颜色设置方式 1、使用颜色的英文单词 2、使用十六进制表示法 3、使用rgb()表示法…

Redis分区/分片详解

分区/分片详解 分区是分割数据到多个Redis实例的处理过程&#xff0c;因此每个实例只保存key的一个子集。 如果只使用一个redis实例时&#xff0c;其中保存了服务器中全部的缓存数据&#xff0c;这样会有很大风险&#xff0c;如果单台redis服务宕机了将会影响到整个服务。解决的…

easyExcel不同版本按照模板导出

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存&#xff0c;poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题&#xff0c;但POI还是有一些缺陷&#xff0c;比如07版Excel解压缩以及解压后存储都是在内存中完成的…