c++:构造函数(Constructor)与析构函数(Destructor)
目录
为什么我们需要构造函数?
什么是构造函数?
🧬 本质:构造函数是“创建对象的一部分”
为什么 需要析构函数?
什么是析构函数?
析构函数的核心作用
❗注意点
为什么我们需要构造函数?
目的:在对象被创建(分配内存)时,自动初始化其成员变量,并完成一些必要的资源准备。
核心本质:
-
构造函数 = 一种特殊的成员函数;
-
自动被调用,不能手动调用;
-
目的是保证对象在“出生”时是有效、完整的。
如果我们创建一个类 Person
,它有名字和年龄两个成员变量。我们每次创建这个对象时都希望它是有意义的、状态合法的。
class Person {
public:
std::string name;
int age;
};
如果你直接创建:
Person p;
这样写当然是可以的,但是存在几个问题:
-
麻烦:每次创建都要写
.name = ...
,.age = ...
,很啰嗦。 -
容易出错:你可能忘记设置某个属性,比如只设置了名字,没有设置年龄。
-
没有保障:谁都可以创建一个“空白”的 Person,然后留下“脏数据”。
这会导致一大堆 bug。
什么是构造函数?
构造函数是一种特殊的函数,在你创建对象的时候,自动调用,用来进行初始化。
关键点:
-
它和类同名。
-
它没有返回值。
-
它在对象创建的那一刻被调用(不是你手动调用,它自动来)。
-
它的存在是为了:让对象一出生就是“有意义的、完整的”状态。
class Person {
public:
std::string name;
int age;
// 构造函数
Person(std::string n, int a) {
name = n;
age = a;
}
};
int main() {
Person p("Alice", 30); // ← 自动调用构造函数
}
你创建对象的时候,构造函数就被调用,把 name 和 age 初始化成你想要的值。
🧬 本质:构造函数是“创建对象的一部分”
你可以这样理解:
Person p("Alice",18);
这不是一句“函数调用”,而是:
“我想创建一个叫 p
的 Person
类型的对象,它的初始状态是 name = "Alice"
,age = 30
”。
而构造函数的作用,就是:
根据你提供的参数,决定这个对象“出生”时是什么样子。
所以构造函数是一种初始化的保证机制。
为什么 需要析构函数?
C++ 是一门**“手动管理资源”**的语言(不像 Python、Java 有垃圾回收),也就是说:
你创建了什么资源,就必须自己负责把它销毁。
想象一个例子:
class MyClass {
public:
int* data;
MyClass() {
data = new int[100]; // 开辟了内存
}
};
你用 new
分配了内存。
然后当你 main()
函数结束时,这个对象不再使用了。但那块 new 出来的内存还留在内存里!
这叫内存泄漏。
如果你每次都得自己写:
delete[] obj.data;
那太痛苦了,而且容易忘记。
于是 C++ 提供了解决方案:析构函数。
什么是析构函数?
析构函数是一个在对象“销毁”时自动调用的特殊函数。
它的作用是:在对象生命结束时,做一些清理工作,比如释放内存、关闭文件、断开连接等。
-
名字和类名相同,但前面有一个
~
(波浪号)。 -
没有参数。
-
没有返回值。
-
不能重载(每个类只有一个析构函数)。
class MyClass {
public:
int* data;
MyClass() {
data = new int[100];
std::cout << "构造函数:分配内存" << std::endl;
}
~MyClass() {
delete[] data;
std::cout << "析构函数:释放内存" << std::endl;
}
};
int main() {
MyClass obj; // 构造函数被调用
} // 离开作用域,析构函数被自动调用
析构函数的核心作用
动作阶段 | 你写的函数 | 系统调用时机 |
---|---|---|
对象创建 | 构造函数 | new 、定义变量时 |
对象销毁 | 析构函数 | delete 、变量离开作用域时 |
常见的清理操作包括:
-
delete
动态分配的内存 -
fclose()
关闭文件 -
断开数据库连接
-
打印日志(对象销毁时记录)
❗注意点
1. 自动对象 vs 动态对象
{
MyClass a; // 离开花括号,析构函数自动被调用
}
MyClass* b = new MyClass();
// 只有当你 delete b; 析构函数才会被调用
所以:用 new 创建的对象必须手动 delete,否则析构函数不会执行!
2. 析构函数不能有参数,也不能重载
析构函数永远只有一个,不能像构造函数那样重载多个版本。
3. 析构函数可以是 virtual
(虚析构)
virtual ~Base() {}
如果你将来会用继承或多态,一定要写虚析构函数,否则可能导致资源无法正确释放(对象“表面”销毁了,内部派生类的资源没被释放)。