析构函数
构造函数(constructor)在创建对象时被系统自动调用,而析构函数(Destructor)在对象被撤销时被自动调用,相比构造函数,析构函数要简单的多。析构函数有如下特点:
- 与类同名,之前冠以波浪号,以区别于构造函数。
- 析构函数没有返回类型,也不能指定参数。因此,析构函数只能有一个,不能被重载。
- 对象超出其作用域被销毁时,析构函数会被自动调用。
析构函数在对象撤销时自动调用,用以执行一些清理任务,如释放成员函数中动态申请的内存等。如果程序员没有显式的定义它,系统也会提供一个默认的析构函数。
下面以一个具体的例子来说明,构造函数与析构函数的进一步功能。在上面的代码当中,我们定义的类当中有一个指针类型的变量。在构造函数的初始化列表当中,对该指针变量进行初始化的时候,使用new方法,开辟了一段堆空间,new表达式申请数组空间时,最后不加() 不会进行初始化.new表达式申请数组空间时,加上() 就会进行初始化然1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
using std::cout;
using std::endl;
class Computer{
public:
Computer(char * brand, float price)
:_brand(new char[strlen(brand)+1]())
,_price(price){
cout << "Computer(const char*, float)" << endl;
strcpy(_brand, brand);
}
~Computer(){
if (_brand) {
delete [] _brand;
_brand=nullptr;
cout << "Destructor ~Computer()" << endl;
}
}
void set_price(float price);
void print_info();
private:
char * _brand;
float _price;
};
void Computer::set_price(float price){
_price = price;
}
void Computer::print_info(){
cout << "brand: " << _brand << endl;
cout << "price: " << _price << endl;
}
void test(){
Computer c1("lenovo", 200);
c1.print_info();
}
int main(int argc, char* argv[])
{
test();
return 0;
}
后在构造函数当中对该指针变量指向的空间进行了赋值。
在析构函数当中,我们需要释放堆空间上申请的变量。
可以看到在该对象的生命周期结束的时候,自动调用了析构函数。
析构函数调用的时机
- 对于全局定义的对象,每当程序开始运行,在主函数main接受程序控制权之前,就调用构造函数创建全局对象,整个程序结束时,自动调用全局对象的析构函数。
- 对于局部定义的对象,每当程序流程到达该对象的定义处就调用构造函数,在程序离开局部对象的作用域时调用对象的析构函数。
- 对于关键字static定义的静态局部变量,当程序流程第一次到达该对象定义处调用构造函数,在整个程序结束时调用析构函数。
- 对于用new运算符创建的对象,每当创建该对象时调用构造函数,当用delete删除该对象时,调用析构函数
下面使用一个例子来说明析构函数的调用时机。
1 |
|
可以看到,我们一共创建了c0~c4 一共5个对象。通过析构函数调用的时机,我们可以看到,析构函数调用的顺序大体如下:
- 使用delete 删除的对象,堆对象生命周期结束
- 在局部作用范围结束,栈对象生命周期结束
- 静态对象在main函数执行退出的时候
- 全局对象在main函数执行退出的时候
析构函数的调用与对象的销毁是一回事吗?
不是一回事,对象销毁的时候,一定会调用析构函数。但是调用析构函数不一定会销毁对象。
析构函数作为一个成员函数,可以被显式地调用。
虽然析构函数可以被显示调用,但是一般情况下不会这么做,也被建议不这样做。
下面的例子当中显式地调用了析构函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using std::cout;
using std::endl;
class Computer{
public:
Computer(const char * brand, float price)
:_brand(new char[strlen(brand)+1]())
,_price(price){
cout << "Computer(const char*, float)" << endl;
strcpy(_brand, brand);
}
~Computer(){
if (_brand) {
cout << "brand:" <<_brand <<" price:" << _price << endl;
delete [] _brand;
_brand=nullptr;
cout << "Destructor ~Computer()" << endl;
}
cout << "Destructor resource free" << endl;
}
void set_price(float price);
void print_info();
private:
char * _brand;
float _price;
};
void Computer::set_price(float price){
_price = price;
}
void Computer::print_info(){
cout << "brand: " << _brand << endl;
cout << "price: " << _price << endl;
}
void test(){
Computer c2("ccc", 300);
c2.~Computer();
cout << "test end" << endl;
}
int main(int argc, char* argv[])
{
test();
cout << "program finished" <<endl;
return 0;
}
可以看到,显式地调用析构函数后,完成了资源的回收。当对象生命周期结束的时候,再次执行了析构函数。