非常感激我队友大爹给我的复习资料

什么是析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

一个类内可以有多个构造函数,可以是一般类型的,也可以是带参数的,相当于重载构造函数。但是析构函数只能有一个。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀。

定义方式:~类名(无参数){}

示例代码

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
#include <iostream>
using namespace std;

class Matrix{
private:
int row;
int col;
public:
Matrix(int r, int c);
Matrix(void);
void setMatrix(int r, int c);
pair<int,int> getMatrix(void);
~Matrix(); // 析构函数声明
};

// 成员函数定义
Matrix::Matrix(void){
cout << "Object is created by Constructor 1" << endl;
}
Matrix::Matrix(int r, int c) : row(r), col(c) {
cout << "Object is created by Constuctor 2" << endl;
}
Matrix::~Matrix(void){
cout << "Object is being deleted" << endl;
}

void Matrix::setMatrix(int r, int c){
row = r, col = c;
}
pair<int,int> Matrix::getMatrix(void){
return pair<int,int>(row, col);
}

int main(void){
Matrix mtr;
pair<int, int> p1 = mtr.getMatrix();
mtr.setMatrix(2, 3);
pair<int, int> p2 = mtr.getMatrix();
Matrix mtr2(4, 5);
pair<int, int> p3 = mtr2.getMatrix();
printf("p1 : (%d, %d), p2 : (%d, %d), p3 : (%d, %d)\n", p1.first, p1.second, p2.first, p2.second, p3.first, p3.second);
return 0;
}

/*
该程序的输出结果为:
===================
Object is created by Constructor 1
Object is created by Constuctor 2
p1 : (50, 0), p2 : (2, 3), p3 : (4, 5)
Object is being deleted
Object is being deleted
===================
*/

析构函数作用

一个类有且仅有一个析构函数,如果定义类时没有写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成默认析构函数。

析构函数在对象消亡时自动被调用,可以定义析构函数在对象消亡前做善后工作。

例如,对象如果在生存期间用new 运算符动态分配了内存,则在各处写delete语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事。有了析构函数,只要在析构函数中调用delete语句,就能确保对象运行中用new运算符分配的空间在对象消亡时被释放。

具体看示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
String类的成员变量p指向动态分配的一片存储空间,用于存放字符串。
动态内存分配在构造函数中进行,而空间的释放在析构函数~String()中进行
*/
class String{
private:
char * p;
public:
String(int n);
~String();
};

String::~String(){
delete[] p;
}
String::String(int n){
p = new char[n];
}

析构函数调用时机

  • 只要对象消亡,就会引发析构函数的调用

  • 传参时(值传递)函数的参数对象以及作为函数返回值的对象,在消亡时也会引发析构函数调用

示例

定义如下类与函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class CDemo{
public:
CDemo(){
cout << "Object created" << endl;
}
CDemo(const CDemo & c){
cout << "copy" << endl;
}
~CDemo(){
cout << "Destructor called" << endl;
}
};

CDemo d1; // 在main函数之前调用构造函数,全局变量
CDemo Test(){
cout << "Test" << endl;
return d1;
}

void Func(CDemo obj){
cout << "func" << endl;
}

一开始先创建了一个全局变量,输出create

Part1

1
2
3
CDemo a[2];                  
CDemo* pTest = new CDemo;
delete pTest;

创建了两个对象,输出两次create

创建一个对象,输出一次create

删除第二句创建的对象,输出一次destroy

1
2
3
4
Object created
Object created
Object created
Destructor called

Part2

1
2
3
CDemo d2; 
Func(d2);
Test();

创建一个对象,输出一次create。

值传递先输出拷贝,然后输出func成功调用的信息,函数结束完调用形参的析构函数输出destroy。

输出test成功调用的信息,返回时先输出拷贝然后返回一个对象,函数结束后调用返回对象的析构函数输出destroy。

1
2
3
4
5
6
7
Object created
copy
func
Destructor called
test
copy
Destructor called

Part3

1
2
3
pTest = new CDemo[2];        
delete[] pTest;
return 0;

创建两个对象,输出两个create

delete两个对象,输出两个destroy

main函数结束,删除Part1中的对象数组a,输出两个destroy;删除Part2中的d2,输出一个destroy。

程序运行结束,删除全局变量d1,输出一个destroy。

1
2
3
4
5
6
7
8
Object created
Object created
Destructor called
Destructor called
Destructor called
Destructor called
Destructor called
Destructor called

源码

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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
/*
1. 一个类有且仅有一个析构函数,如果定义类时没有写析构函数,则编译器生成默认析构函数。如果定义了析构函数,则编译器不生成默认析构函数。
2. 析构函数在对象消亡时自动被调用,可以定义析构函数在对象消亡前做善后工作。
例如,对象如果在生存期间用new 运算符动态分配了内存,则在各处写delete语句以确保程序的每条执行路径都能释放这片内存是比较麻烦的事。有了
析构函数,只要在析构函数中调用delete语句,就能确保对象运行中用new运算符分配的空间在对象消亡时被释放。
3. 析构函数被调用的时机
1) 只要对象消亡,就会引发析构函数的调用
2) 函数的参数对象以及作为函数返回值的对象,在消亡时也会引发析构函数调用
*/
#include <iostream>
using namespace std;

/*
String类的成员变量p指向动态分配的一片存储空间,用于存放字符串。动态内存分配在构造函数中进行,而空间的释放在析构函数~String()中进行
*/
class String{
private:
char* p;
public:
String(int n);
~String();
};

String::~String(){
delete[] p;
}
String::String(int n){
p = new char[n];
}


class CDemo{
public:
CDemo(){
cout << "Object created" << endl;
}
CDemo(const CDemo & c){
cout << "copy" << endl;
}
~CDemo(){
cout << "Destructor called" << endl;
}
};

CDemo d1; // 在main函数之前调用构造函数
CDemo Test(){
cout << "Test" << endl;
return d1;
}

void Func(CDemo obj){
cout << "func" << endl;
}

int main(void){
cout << "Main begins." << endl;
CDemo a[2]; // 构造函数调用2次
CDemo* pTest = new CDemo; // 构造函数调用
delete pTest; // 析构函数调用 delete使得动态分配的CDemo对象消亡
cout << "------------" << endl;

CDemo d2; // main函数中的局部变量, 调用构造函数1次
Func(d2); // Func函数开始时,d2以值传递的方式传入函数参数, 调用拷贝构造函数;Func结束时, 参数对象obj消亡, 调用析构函数
Test(); // Test函数的返回值是一个临时对象, 该临时对象在函数调用所在的语句结束时就消亡了, 因此引发析构函数调用
cout << "after Test." << endl;

cout << "------------" << endl;
pTest = new CDemo[2]; // 构造函数调用2次 动态分配数组, 两个元素, 构造2次
delete[] pTest; // 析构函数调用2次 delete释放动态分配的数组pTest, 数组中有两个CDemo对象消亡
cout << "Main ends." << endl; // 3次析构函数调用发生在 main 函数结束时 1) a数组中两个元素消亡 d2消亡

return 0; // 最后1次调用析构函数, 整个程序结束时, 全局变量d1消亡
}

/*
该程序的输出结果为:
===================
Object created
Main begins.
Object created
Object created
Object created
Destructor called
------------
Object created
copy
func
Destructor called
Test
copy
Destructor called
after Test.
------------
Object created
Object created
Destructor called
Destructor called
Main ends.
Destructor called
Destructor called
Destructor called
Destructor called
===================
*/