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

相关推荐
历程里程碑4 小时前
54 深入解析poll多路复用技术
java·linux·服务器·开发语言·前端·数据结构·c++
无限进步_4 小时前
【C++】可变参数模板与emplace系列
java·c++·算法
计算机安禾5 小时前
【c++面向对象编程】第28篇:new/delete vs malloc/free:C++中正确动态内存管理
开发语言·c++·算法
qeen875 小时前
【算法笔记】各种常见排序算法详细解析(下)
c语言·数据结构·c++·笔记·学习·算法·排序算法
计算机安禾5 小时前
【c++面向对象编程】第32篇:移动语义与右值引用:现代C++性能优化核心
java·c++·性能优化
fish_xk5 小时前
c++11的初见
开发语言·c++·算法
不知名的老吴6 小时前
C++ 中函数对象的形式概述
开发语言·c++
Shan12056 小时前
C++中函数对象之重载 operator()
开发语言·c++·算法
djarmy7 小时前
一级函数头地址指针,(*p_func)的函数头的返回值,(*p_func)的函数头的参数列表
c++