1.析构函数的概念
我们在写代码时很清楚的知道一个对象是怎么创建的,但是他又是怎么销毁的呢?
在C++中我们会有默认的构造函数,相对应的也会有默认的销毁类型的函数,我们称这个函数为析构函数。
析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由 编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。
从这一段反汇编我们可以清晰看出来这一段简单的类的创建的代码虽然简短,但是五脏俱全,从开始的创建到最后的销毁。我们分明没有写出销毁函数,但是编译器会自动执行析构函数进行销毁。
2.析构函数的使用
- 析构函数名是在类名前加上字符 ~。(不要问为啥,问就是给祖师爷打电话问问他怎么想的)
- 无参数无返回值类型。
- 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构 函数不能重载
- 对象生命周期结束时,C++编译系统系统自动调用析构函数。
typedef int DateType;
class Stack
{
public:
Stack(int capacity=3)
{
_array =(DateType*) malloc(sizeof(DateType) * capacity);
if (_array == NULL)
{
perror("malloc fail");
}
_capacity = capacity;
_size = 0;
}
~Stack()
{
if (_array)
{
free(_array);
_size = 0;
_capacity = 0;
_array = NULL;
}
}
void push(DateType x)
{
_array[_size++] = x;
}
private:
int _size;
int _capacity;
DateType* _array;
};
int main()
{
Stack S;
S.push(1);
S.push(2);
return 0;
}
3.析构函数的注意事项
当然,析构函数作为与构造函数相对的函数,也是有一些共同点的。对于在类中有自定义类型的时候,编译器会自动调用这个自定义类型的析构函数。
private:
int _size;
int _capacity;
DateType* _array;
Time _t;
};
int main()
{
Stack S;
S.push(1);
S.push(2);
return 0;
}
我们很清晰的看出在return 0之后先调用这个顺序表的析构,之后再去销毁这个自定义类型,那这个析构函数是谁的呢?
main方法中创建了Stack对象S,而S中包含4个成员变量,其中_size, _capacity, 两个是内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是: main函数中不能直接调用Time类的析构函数,实际要释放的是Stack类对象,所以编译器会调用Stac类的析构函数,而Stack没有显式提供,则编译器会给Stack类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Stack对象销毁时,要保证其内部每个自定义对象都可以正确销毁main函数中并没有直接调用Time类析构函数,而是显式调用编译器为Stack类生成的默认析构函数。注意:创建哪个类的对象则调用该类的析构函数,销毁那个类的对象则调用该类的析构函数。
注意: 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。