19 析构函数
析构函数也是一种特殊的成员函数,它的作用与构造函数相反,是在撤销对象占用的内存空间之前完成一些清理工作,使得这部分内存可以被程序重新分配。
1 语法
与构造函数相似,析构函数也没有函数类型,没有返回值。 与构造函数不同的是,析构函数不可以有参数,而且一个类中只能有一个析构函数,不能重载。
在类中定义析构函数的语法如下:
~类名(){
函数体
}
2 默认析构函数
与构造函数相同,如果我们没有在类中显式定义析构函数,那么编译器会自动产生一个默认构造函数,可以认为,这是一个函数体为空的析构函数。
~类名(){}
3 析构函数的调用
从析构函数的语法,可以知道, 析构函数是没有函数名的。 这是因为, 析构函数是对象生命周期结束时,由程序自动调用的,不能被显式调用。
#include<iostream>
using namespace std;
class Student {
private:
string name;
int age;
int score;
public:
Student(string _name, int _age, int _score) : name(_name), age(_age > 0 ? _age : 0), score(_score) {
cout << "调用" << name << "的构造函数" << endl;
}
~Student(){
cout << "调用" << name << "的析构函数" << endl;
}
};
int main() {
Student stu1("张三", 16, 85), stu2("李四", 18, 90);
cout << "main函数" << endl;
return 0;
}
程序输出:
调用张三的构造函数
调用李四的构造函数
main函数
调用李四的析构函数
调用张三的析构函数
从程序的结果,可以看出 析构函数的调用顺序与构造函数相反,先构造的对象后析构;后构造的函数先析构。
4 析构函数的使用场景
在我们之前的学习中,我们编写的类,都没有析构函数,它们运行也没有任何问题。
实际上, 一般来说,我们并不需要为类显式定义析构函数。 使用编译器自动生成的默认析构函数即可。
但是,同学们还记得 动态存储分配 吗?
通过动态存储分配获得到的变量,必须使用 delete
关键字,显式地进行销毁,这个变量所占用的内存空间才会被释放。否则,将会导致 内存泄漏 。这会使电脑运行缓慢,甚至电脑崩溃。
在类的构造函数中,我们同样可以使用 new
关键字,对类的成员数据使用动态存储分配。如果我们这样做了,那么我们就一定要在析构函数中,使用 delete
关键字将此数据释放,以避免内存泄漏。
在下面的例子中,score
属性不在只是存储一个成绩数据,而是存储多个数据,比如语文、数学、外语三门学科的成绩,组成一个数组,并使用动态存储分配。
#include<iostream>
using namespace std;
class Student {
private:
string name;
int age;
// 数组指针属性
int* score;
public:
// 在构造函数中,初始化动态存储分配的数组
Student(string _name, int _age, int _score[]) : name(_name), age(_age > 0 ? _age : 0) {
score = new int[3];
for (int i = 0; i < 3; ++i) {
*(score + i) = _score[i];
}
}
// 在析构函数中,销毁动态存储分配的数组
~Student(){
delete []score;
}
};
int main() {
int score[3] = {98, 90, 85};
Student stu1("张三", 16, score);
cout << "main函数" << endl;
return 0;
}