析构函数的概念
析构函数的特性
1、析构函数名是在类名前加上字符 ~。
2、无参数无返回值类型。
3、一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载。
4、对象生命周期结束时,C++编译系统系统自动调用析构函数。
以下三种情况是否需要写析构函数:
有动态申请资源 ,需要显式写析构函数
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
class Stack
{
public:
Stack(int defaultcapacity=4)
{
cout << "Stack(int defaultcapacity=4)" << endl;
_a = (int*)malloc(sizeof(int) * defaultcapacity);
if (_a == nullptr)
{
perror("malloc fail");
return;
}
_top = 0;
_capacity = defaultcapacity;
}
void push(int x)
{
_a[_top] = x;
_top++;
}
void pop()
{
_top--;
}
void print()
{
cout << _a[_top-1] << endl;
}
void Destroy()
{
cout << "estroy()" << endl;
free(_a);
_a = nullptr;
_top = _capacity = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
int main()
{
Stack st1;
st1.Destroy();
return 0;
}
最后打印的结果是
可以看到,我们调用了Destroy函数,所以打印了estroy,那如果我们不调用呢?系统会自己生成一个给我们吗?看下面的情况。
看框中的代码,只创建了一个成员,调试到目前为止,系统自动调用构造函数对st1进行了初始化。需要注意的是,在上述代码中,我们写了构造函数Stack,但是没有写析构函数,以观察系统是否会自动生成。
根据上图,我们发现,尽管已经出了函数的作用域,但是却并没有清理,表示系统没有自己生成并调用析构函数。基于此,我们如果写一个不是内置类型的变量,是需要自己写析构函数的。
观察下图,我们自己写了一个析构函数,但是没有调用,看看系统会不会自动调用析构函数。
调试到此,表示系统自动调用了构造函数。
根据打印的结果和调试的结果可以看出,系统自动调用了析构函数。
总结:有动态申请资源的,需要自己写析构函数,但是系统会帮你自动调用。
无动态申请资源,不需要写析构函数
class Data
{
public:
Data(int year=2,int month=2,int day=2)
{
_year = year;
_month = month;
_day = day;
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
//没有动态申请资源,都是内置类型,栈帧结束,系统自动回收,不用写析构函数
private:
int _year;
int _month;
int _day;
};
int main()
{
Data d1;
d1.print();
return 0;
}
对于像日期类这种没有动态申请资源,全是内置类型的类,不需要写析构函数,因为都是存在栈区的,调用结束,系统自动回收。
原理很简单,他们是在栈区的,本来就不需要进行Destroy。
总结:对于没有动态申请资源的,不需要写析构函数。
需要释放成员都是自定义类型,不需要写析构函数,系统会调用它的析构函数(构造函数同理)
观察下面的代码,是用两个栈创建一个队列,成员都是Stack,所以系统会自动调用构造和析构函数,进入Stack,Stack里面有构造和析构函数,所以会自动调用,最后给到这个q1。
看下面的图,明明我们创建的是Myqueue的变量,最后自动调用的是Stack的构造函数和析构函数。
这张图表示,我们进入调试,发现系统跳到了Stack的构造函数那里,表示Myqueue的构造函数是由Stack实现的,因为,Myqueue是由Stack实现的。
这张图表示,进入调试,系统自动跳到了Stack的析构函数那里,表示最终由Stack的析构函数实现。
总结:对于需要释放资源的成员都是自定义成员的,不需要写析构函数,系统会自动调用。
构造函数和析构函数都是:不要你再去手动调用了,系统会自动调用,解决了忘记调用的问题。