代码仓库shanchuann/CPP-Learninng

在C++中,构造函数和析构函数是类的特殊成员函数,分别用于对象的初始化和资源清理,它们由编译器自动调用,是面向对象编程中管理对象生命周期的核心机制。

this指针

编译器对类型编译的过程中分为三个部分

  1. **类型解析:**编译器首先对代码中的类型声明进行解析,识别类型的定义、成员结构(如类的成员变量和成员函数)、继承关系等信息
  2. 函数识别:在语义分析阶段,编译器基于类型解析的结果,对程序中的函数返回类型、函数名、参数类型进行识别
  3. **成员方法改写:**无论成员方法是公有私有,他会对非静态成员方法加入常性this指针。
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
class Pointer {
private:
int row, col;
public:
void SetRow(int r) {
row = r;
}
/* void SetRow(Pointer *const this,int r){
this->row = r;
}
*/
void SetCol(int c) {
col = c;
}
int GetRow() const;
int GetCol() const;
};
int Pointer::GetRow() const{
return row;
}
int Pointer::GetCol() const{
return col;
}
int main() {
Pointer pa,pb,pc,pd;
pa.SetRow(10); //SetRow(&pa,10);
pb.SetRow(20); //SetRow(&pb,20);
pc.SetCol(30); //SetCol(&pc,30);
pd.SetCol(40); //SetCol(&pd,40);
return 0;
}

image-20251021182535279

构造函数(Constructor)

构造函数是创建对象时自动调用的成员函数,主要作用是初始化对象的成员变量(如给成员变量赋初值、分配动态内存等)。

核心特点

  • 名字与类名完全相同:例如类名为Person,构造函数名也必须是Person
  • 无返回值:不能写void或其他返回类型(连return语句都不能带返回值)。
  • 可重载:可以定义多个参数不同的构造函数(参数个数或类型不同),满足不同的初始化需求。
  • 自动调用:创建对象时(如Person p;new Person();)由编译器自动调用,无需手动调用。
  • 一次调用:生存期内构造函数只能被调用一次,但可以重载构造函数。

默认构造函数

如果用户没有定义任何构造函数,编译器会自动生成一个默认构造函数(无参数,函数体为空)。
但如果用户定义了构造函数(无论是否有参数),编译器不会再生成默认构造函数

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

class Person {
private:
string name;
int age;
public:
// 无参构造函数(默认构造的手动实现)
Person() {
name = "Unknown";
age = 0;
cout << "无参构造函数被调用" << endl;
}

// 带参构造函数(重载)
Person(string n, int a) {
name = n;
age = a;
cout << "带参构造函数被调用" << endl;
}

void show() {
cout << "Name: " << name << ", Age: " << age << endl;
}
};

int main() {
Person p1; // 调用无参构造函数
p1.show(); // 输出:Name: Unknown, Age: 0

Person p2("Tom", 18); // 调用带参构造函数
p2.show(); // 输出:Name: Tom, Age: 18

return 0;
}
1
2
3
4
无参构造函数被调用
Name: Unknown, Age: 0
带参构造函数被调用
Name: Tom, Age: 18

析构函数(Destructor)

析构函数是对象生命周期结束时自动调用的成员函数,主要作用是清理对象占用的资源(如释放动态内存、关闭文件句柄等)。

核心特点

  • 名字为~类名:例如类名为Person,析构函数名是~Person
  • 无返回值:同构造函数,不能写返回类型。
  • 无参数:因此不能重载(一个类只能有一个析构函数)。
  • 自动调用:对象销毁时(如局部对象离开作用域、delete动态对象时)由编译器自动调用。

默认析构函数

如果用户没有定义析构函数,编译器会自动生成默认析构函数(函数体为空)。但如果类中使用了动态内存(如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
#include <iostream>
using namespace std;

class Array {
private:
int* data; // 动态数组
int size;
public:
// 构造函数:分配动态内存
Array(int s) {
size = s;
data = new int[size]; // 分配内存
cout << "构造函数:分配了" << size << "个int的内存" << endl;
}

// 析构函数:释放动态内存
~Array() {
delete[] data; // 释放内存(必须与new[]对应)
cout << "析构函数:释放了动态内存" << endl;
}

void set(int index, int value) {
if (index >= 0 && index < size) {
data[index] = value;
}
}
};

int main() {
{
Array arr(5); // 创建对象,调用构造函数
arr.set(0, 100);
} // 离开作用域,对象销毁,自动调用析构函数(释放内存)

return 0;
}

输出:

1
2
构造函数:分配了5个int的内存
析构函数:释放了动态内存

构造与析构的调用顺序

  • 构造函数:创建对象时,先调用基类的构造函数,再调用成员对象的构造函数,最后调用自身的构造函数
  • 析构函数:对象销毁时,调用顺序与构造相反:先调用自身的析构函数,再调用成员对象的析构函数,最后调用基类的析构函数

关键区别

特性 构造函数 析构函数
作用 初始化对象(分配资源) 清理对象(释放资源)
名字 与类名相同 ~类名
参数 可以有(可重载) 无(不可重载)
调用时机 对象创建时 对象销毁时
默认版本 无自定义时自动生成 无自定义时自动生成