C++学习笔记17:析构函数

目录

一、什么是析构函数?

二、析构函数写法

三、析构函数的特点

四、析构函数什么时候调用?

五、析构函数不是销毁对象本身

六、为什么需要析构函数?

七、用析构函数释放动态内存

八、析构函数的调用顺序

九、析构函数和构造函数的区别

十、什么时候必须写析构函数?

十一、小结


一、什么是析构函数?

析构函数是C++类中的一种特殊成员函数。

它会在队形生命周期结束时自动调用,主要作用是:

清理对象在使用过程中申请的资源。

比如一个类中如果动态申请了内存,那么在对象销毁前,就应该把这块内存释放掉,避免内存泄露。

析构函数和构造函数刚好对应:

cpp 复制代码
构造函数:对象创建时自动调用,用来初始化对象
析构函数:对象销毁时自动调用,用来清理资源

二、析构函数写法

析构函数的函数名是在类名前面加 ~。

基本格式;

cpp 复制代码
~类名() {
    // 清理资源
}

例如:

cpp 复制代码
class Date {
	public:
		~Date() {
			cout << "Date destructor" << endl;
		}
}; 

这里:

cpp 复制代码
~Date()

就是 Date 的析构函数。


三、析构函数的特点

析构函数有几个重要的特点:

cpp 复制代码
1. 函数名是在类名前加 ~;
2. 没有返回值;
3. 没有参数;
4. 不能重载;
5. 对象销毁时自动调用。

因为析构函数没有参数,所以一个类中只能有一个析构函数,不能像构造函数那样重载。


四、析构函数什么时候调用?

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

例如局部对象离开作用域时:

cpp 复制代码
#include <iostream>
using namespace std;

class Date {
	public:
		Date() {
			cout << "Date constructor" << endl;
		}
		
		~Date() {
			cout << "Date destructor" << endl;
		}
};

int main() {
	Date d;
	
	return 0;
}

运行结果:

cpp 复制代码
Date constructor
Date destructor

当执行:

cpp 复制代码
Date d;

时,构造函数自动调用。

当 main 函数结束,对象 d 生命周期结束时,析构函数自动调用。


五、析构函数不是销毁对象本身

这一点很重要。

析构函数并不是负责把对象本是谁呢销毁。

对象空间的创建和释放由编译器管理,析构函数主要负责清理对象内部申请的资源。

比如:

cpp 复制代码
class Stack {
	private:
		int* _array;
};

如果 _array 指向的是动态申请的空间,那么对象销毁前,就应该释放这块空间。

所以析构函数更准确的作用是:

清理对象持有的资源,而不是销毁对象本身。


六、为什么需要析构函数?

假设我们写一个栈类,在初始化时动态申请空间:

cpp 复制代码
class Stack {
	public:
		void Init(int capacity) {
			_array = (int*)malloc(sizeof(int) * capacity);
			_top = 0;
			_capacity = capacity;
		}
	
	private:
		int* _array;
		int _top;
		int _capacity; 
};

如果对象用完后不是放 _array 指向的空间,就会造成内存泄漏。

在C语言中,我们通常要手动写 Destory 函数:

cpp 复制代码
void Destory() {
	free(_array);
	_array = nullptr;
	_top = 0;
	_capacity = 0;
}

但是这样有一个问题:

cpp 复制代码
如果忘记手动调用 Destory,就可能导致内存泄漏。

C++中可以把资源清理工作放到析构函数里,让对象销毁时自动完成清理。


七、用析构函数释放动态内存

示例:

cpp 复制代码
#include <iostream>
using namespace std;

class Stack {
	public:
		Stack(int capacity = 4) {
			_array = (int*)malloc(sizeof(int) * capacity);
			_top = 0;
			_capacity = capacity;
		}
		
		~Stack() {
			if (_array != nullptr) {
				free(_array);
				_array = nullptr;
			}
			
			_top = 0;
			_capacity = 0;
			
			cout << "Stack destructor" << endl;
		}
	
	private:
		int* _array;
		int _top;
		int _capacity; 
};

int main() {
	Stack s;
	
	return 0;
} 

当 s 生命周期结束时,析构函数会自动调用,释放 _array 指向的动态空间。

这样就不用手动调用 Destory 了。


八、析构函数的调用顺序

如果在同一个作用域中创建多个对象,析构函数通常和构造函数相反。

例如:

cpp 复制代码
#include <iostream>
using namespace std;

class Date {
	public:
		Date(int id) {
			_id = id;
			cout << "Date " << _id << " constructor" << endl;
		}
		
		~Date() {
			cout << "Date " << _id << " destructor" << endl;
		}
	
	private:
		int _id;
};

int main() {
	Date d1(1);
	Date d2(2);
	
	return 0;
}

运行结果:

cpp 复制代码
Date 1 constructor
Date 2 constructor
Date 2 destructor
Date 1 destructor

可以看到:

cpp 复制代码
先构造 d1,再构造 d2;
先析构 d2,再析构 d1。

这符合栈的特点,后创建的对象先销毁。


九、析构函数和构造函数的区别

对比项 构造函数 析构函数
调用时机 对象创建时 对象销毁时
主要作用 初始化对象 清理资源
函数名 与类名相同 类名前加 ~
返回值 没有返回值 没有返回值
参数 可以有参数 不能有参数
是否能重载 可以重载 不能重载
调用方式 自动调用 自动调用

构造函数和析构函数共同管理对象的生命周期。


十、什么时候必须写析构函数?

如果类中没有申请资源,一般可以不写析构函数,让编译器自动生成即可。

例如:

cpp 复制代码
class Date {
    private:
        int _year;
        int _month;
        int _day;
};

这种类只有普通成员变量,不涉及动态资源,通常不需要自己写析构函数。

但是类中有:

cpp 复制代码
动态申请的内存
打开的文件
网络连接
系统资源

就应该考虑写析构函数进行释放。

比如:

cpp 复制代码
int* _array;

如果它指向 malloc 或 new 申请的空间,就需要在析构函数中释放。


十一、小结

本篇主要学习了C++中的析构函数。

需要记住:

  1. 析构函数是在类名前加 ~;
  2. 析构函数没有返回值;
  3. 析构函数没有参数;
  4. 析构函数不能重载;
  5. 析构函数再对象销毁时自动调用;
  6. 析构函数主要用于清理对象持有的资源;
  7. 析构函数不销毁对象本身;
  8. 如果类中动态申请了内存,通常需要在析构函数中释放;
  9. 多个局部对象的析构顺序通常和构造顺序相反。

析构函数是C++类和对象中的重要内容,和构造函数一起构成了对象的生命周期管理的基础。后面学习拷贝构造、赋值运算符重载、动态内存管理时,还会继续用到它。

相关推荐
凡人叶枫18 小时前
Effective C++ 条款02:宁可以编译器替换预处理器
java·linux·c语言·开发语言·c++
AC赳赳老秦18 小时前
用 OpenClaw 制定技术学习计划:根据目标岗位自动生成学习路线、推荐学习资源
开发语言·c++·人工智能·python·mysql·php·openclaw
ShineWinsu19 小时前
对于Linux:内核是如何组织管理IPC资源的解析
linux·服务器·c++·面试·笔试·线程·ipc
少司府19 小时前
C++进阶:红黑树
开发语言·数据结构·c++·b树·二叉树·红黑树
汉克老师20 小时前
GESP6级C++考试语法知识(五十五、动态规划----背包问题(八、混合背包)
c++·动态规划·dp·背包问题·gesp六级·混合背包问题
特种加菲猫20 小时前
哈希表的实现
开发语言·c++
玖釉-20 小时前
nvpro_core2 详解:NVIDIA Vulkan / OpenGL 图形样例背后的现代 C++ 基础库
c++·windows·图形渲染
不会C语言的男孩20 小时前
C++ Primer 第19章:特殊工具与技术
数据结构·c++
不会C语言的男孩20 小时前
C++ Primer 第18章:用于大型程序的工具
开发语言·c++
星恒随风20 小时前
C++ 类和对象入门(三):拷贝构造、赋值运算符重载和深浅拷贝
开发语言·c++·笔记·学习