在 C++ 面向对象编程中,类(Class)和对象(Object)是核心内容,而构造函数与析构函数则是类中最重要的两个特殊成员函数。它们负责对象的"出生"和"销毁",直接影响程序的资源管理和对象生命周期。
本文将系统整理本节课中关于构造函数与析构函数的核心知识点,并结合代码案例进行分析。
一、构造函数(Constructor)
1. 什么是构造函数
构造函数是一种特殊的成员函数,用于在对象创建时完成初始化工作。
特点:
-
函数名与类名相同
-
没有返回值
-
对象创建时自动调用
-
可以重载
基本语法
class 类名
{
public:
类名()
{
// 初始化代码
}
};
2. 为什么需要构造函数
对象创建后,如果成员变量没有初始化,就会出现随机值。
例如:
class Date
{
public:
int _year;
int _month;
int _day;
};
int main()
{
Date d;
cout << d._year << endl;
}
此时输出的是随机值。
因此,需要构造函数对对象进行初始化。
二、构造函数的分类
1. 无参构造函数(默认构造)
class Date
{
public:
Date()
{
_year = 2025;
_month = 5;
_day = 11;
}
private:
int _year;
int _month;
int _day;
};
调用方式
Date d1;
注意:
Date d1();
这不是创建对象,而是函数声明。
2. 带参构造函数
可以在创建对象时直接传入数据。
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
调用方式
Date d1(2025, 5, 11);
3. 缺省参数构造函数
class Date
{
public:
Date(int year = 2025, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
private:
int _year;
int _month;
int _day;
};
调用方式
Date d1;
Date d2(2024, 10, 1);
三、构造函数的特性
1. 构造函数可以重载
class Test
{
public:
Test()
{
cout << "无参构造" << endl;
}
Test(int x)
{
cout << "带参构造" << endl;
}
};
调用
Test t1;
Test t2(10);
2. 对象创建时自动调用
Date d1;
对象一创建:
-
自动调用构造函数
-
不需要手动调用
3. 如果没有写构造函数
编译器会自动生成默认构造函数。
class A
{
private:
int x;
};
系统会自动生成:
A()
{
}
但是:
-
内置类型不会初始化
-
自定义类型会调用其默认构造
四、初始化列表
1. 什么是初始化列表
初始化列表是构造函数初始化成员变量的一种方式。
语法
class Date
{
public:
Date(int year, int month, int day)
: _year(year)
, _month(month)
, _day(day)
{
}
private:
int _year;
int _month;
int _day;
};
2. 为什么推荐使用初始化列表
因为:
-
效率更高
-
某些成员必须使用初始化列表
例如:
const成员
const int _a;
引用成员
int& _ref;
没有默认构造的对象成员
class A
{
public:
A(int a)
{}
};
class B
{
public:
B(int b)
: _aa(b)
{}
private:
A _aa;
};
五、析构函数(Destructor)
1. 什么是析构函数
析构函数用于对象销毁时释放资源。
特点:
-
函数名前加
~ -
无参数
-
无返回值
-
一个类只能有一个析构函数
-
对象生命周期结束时自动调用
2. 基本语法
class Test
{
public:
~Test()
{
cout << "析构函数调用" << endl;
}
};
3. 析构函数的作用
主要用于:
-
释放动态内存
-
关闭文件
-
释放系统资源
例如:
class Stack
{
public:
Stack(int n = 10)
{
_a = (int*)malloc(sizeof(int) * n);
_capacity = n;
_top = 0;
}
~Stack()
{
free(_a);
_a = nullptr;
_capacity = 0;
_top = 0;
}
private:
int* _a;
int _top;
int _capacity;
};
六、构造函数与析构函数的调用顺序
1. 单个对象
Test t;
执行顺序:
构造函数
析构函数
2. 多个对象
Test t1;
Test t2;
执行顺序:
构造 t1
构造 t2
析构 t2
析构 t1
规律:
-
先构造的后析构
-
类似栈结构(先进后出)
七、隐式生成的成员函数
C++ 编译器默认会生成:
-
默认构造函数
-
析构函数
-
拷贝构造
-
赋值运算符重载
其中:
默认析构函数
如果类中没有动态资源:
系统默认析构即可。
如果类中有动态申请资源:
必须自己写析构函数。
八、经典问题分析
1. 为什么析构函数不能重载?
因为对象销毁时:
编译器无法判断调用哪一个析构函数。
所以:
- 一个类只能有一个析构函数
2. 构造函数能否是 void?
不能。
构造函数没有返回值。
写成:
void Test()
这已经是普通成员函数。
3. 构造函数可以被手动调用吗?
理论上可以:
t.Test();
但不推荐。
因为对象初始化只应该进行一次。
九、总结
构造函数与析构函数是 C++ 类和对象中最核心的内容之一。
构造函数
负责:
-
对象初始化
-
建立对象状态
重点掌握:
-
默认构造
-
带参构造
-
初始化列表
-
构造函数重载
析构函数
负责:
-
对象销毁
-
资源释放
重点掌握:
-
自动调用机制
-
动态资源释放
-
生命周期管理
十、学习建议
学习构造与析构时,建议重点理解:
-
对象生命周期
-
自动调用机制
-
初始化列表本质
-
动态内存管理
-
RAII思想
因为后续:
-
拷贝构造
-
运算符重载
-
STL
-
智能指针
都会建立在这些基础之上。
结语
构造函数和析构函数看似只是两个特殊函数,但它们本质上体现了 C++ 对"资源管理"和"对象生命周期"的核心思想。
真正理解它们之后,才能深入掌握 C++ 面向对象编程。