📋 本期学习目标
- 理解什么是构造函数和析构函数
- 学会如何为类编写构造函数和析构函数
- 掌握构造函数和析构函数的调用时机
- 能够在实际项目中正确使用这两个特殊函数
🎯 本期的核心:两个特殊的"管家"函数
构造函数:对象的“出生仪式”
析构函数:对象的“善后工作”
生活比喻一:玩具的“出厂设置”和“回收利用”
想象一下,你买了一个新的遥控汽车:
- 出厂时:工厂会为它安装电池、设置遥控频率、贴上标签(这就是构造函数的工作)
- 使用后:电池没电了,你要取出电池,清洁车身,放回盒子(这就是析构函数的工作)
生活比喻二:租借图书馆的书
- 借书时:管理员登记你的信息、记录借书日期、检查书的状态(构造函数)
- 还书时:管理员检查书是否完好、更新库存记录、重新上架(析构函数)
🏗️ 构造函数:对象的“出生仪式”
什么是构造函数?
构造函数是一种特殊的成员函数,它在创建对象时自动调用,用来初始化对象的数据成员。
构造函数的三个特点:
- 与类同名
- 没有返回值类型(连void都没有)
- 自动调用(创建对象时自动执行)
代码示例:第一个构造函数
#include <iostream>
#include <string>
using namespace std;
// 小猫类
class Cat {
private:
string name; // 名字
int age; // 年龄
string color; // 颜色
public:
// 构造函数:小猫的“出生仪式”
Cat() {
cout << "一只小猫诞生了!" << endl;
name = "未命名";
age = 0;
color = "未知颜色";
}
// 带参数的构造函数:定制小猫
Cat(string catName, int catAge, string catColor) {
cout << "一只定制小猫诞生了!" << endl;
name = catName;
age = catAge;
color = catColor;
}
// 其他成员函数...
void meow(){
cout << name << ":喵喵喵!" << endl;
}
void showInfo(){
cout << "名字:" << name << endl;
cout << "年龄:" << age << "岁" << endl;
cout << "颜色:" << color << endl;
}
};
int main(){
// 使用默认构造函数创建小猫
Cat cat1; // 自动调用 Cat() 构造函数
cat1.showInfo();
cat1.meow();
cout << "----------------" << endl;
// 使用带参数的构造函数创建小猫
Cat cat2("小白", 2, "白色"); // 自动调用带参数的构造函数
cat2.showInfo();
cat2.meow();
return 0;
}
孩子可以这样理解:
“构造函数就像小猫的出生证明。当一只小猫出生时(创建对象),系统会自动填写它的名字、年龄、颜色等信息。如果没有提供具体信息,就填写默认值(‘未命名’、0岁等)。”
🧹 析构函数:对象的“善后工作”
什么是析构函数?
析构函数也是一种特殊的成员函数,它在对象销毁时自动调用,用来清理对象占用的资源。
析构函数的四个特点:
- 在类名前加波浪号
~(如 ~Cat())
- 没有返回值类型(连void都没有)
- 没有参数
- 自动调用(对象销毁时自动执行)
代码示例:第一个析构函数
#include <iostream>
#include <string>
using namespace std;
class Cat {
private:
string name;
int age;
string color;
public:
// 构造函数
Cat(string catName, int catAge, string catColor) {
name = catName;
age = catAge;
color = catColor;
cout << name << "小猫诞生了!" << endl;
}
// 析构函数:小猫的“善后工作”
~Cat() {
cout << name << "小猫离开了..." << endl;
// 这里可以添加清理工作,比如:
// 1. 关闭文件
// 2. 释放内存
// 3. 断开网络连接
}
void showInfo(){
cout << "名字:" << name << endl;
cout << "年龄:" << age << "岁" << endl;
cout << "颜色:" << color << endl;
}
};
int main(){
cout << "=== 程序开始 ===" << endl;
// 创建小猫对象
{
cout << "--- 进入小猫的专属区域 ---" << endl;
Cat cat1("小花", 3, "花色");
cat1.showInfo();
cout << "--- 小猫的专属区域结束 ---" << endl;
// 这里cat1会自动销毁,调用析构函数
}
cout << "=== 程序结束 ===" << endl;
return 0;
}
孩子可以这样理解:
“析构函数就像小猫离开前的告别仪式。当小猫要离开时(对象销毁),系统会自动做一些清理工作,比如关闭它打开的门窗、清理它留下的食物等。这体现了C++的RAII机制的重要性。”
🎮 趣味练习一:小猫出生记
练习目标
创建一个Cat类,包含构造函数和析构函数,观察它们的调用时机。
代码框架
#include <iostream>
#include <string>
using namespace std;
class Cat {
private:
string name;
int age;
public:
// TODO:在这里添加构造函数
// TODO:在这里添加析构函数
void play(){
cout << name << "正在玩毛线球..." << endl;
}
void sleep(){
cout << name << "正在睡觉..." << endl;
}
};
int main(){
cout << "=== 小猫的一天 ===" << endl;
// 创建小猫对象
Cat myCat("咪咪", 2);
// 小猫的活动
myCat.play();
myCat.sleep();
cout << "=== 一天结束了 ===" << endl;
return 0;
}
完成步骤
- 为
Cat类添加一个带参数的构造函数,接收名字和年龄
- 为
Cat类添加一个析构函数
- 运行程序,观察输出
- 思考:构造函数和析构函数各调用了多少次?
🎮 趣味练习二:魔法宝箱游戏
游戏规则
创建一个MagicBox类,当宝箱打开时(构造函数)获得随机宝物,当宝箱关闭时(析构函数)归还宝物。
完整代码
#include <iostream>
#include <string>
#include <cstdlib>
#include <ctime>
using namespace std;
class MagicBox {
private:
string treasure; // 宝物名称
bool isOpen; // 宝箱是否打开
public:
// 构造函数:打开宝箱,获得宝物
MagicBox() {
srand(time(0)); // 设置随机种子
// 随机选择宝物
int choice = rand() % 5;
string treasures[] = {"金币", "魔法药水", "宝剑", "魔法书", "宝石"};
treasure = treasures[choice];
isOpen = true;
cout << "宝箱打开了!里面是:" << treasure << endl;
}
// 析构函数:关闭宝箱,归还宝物
~MagicBox() {
if (isOpen) {
cout << "宝箱关闭了,归还了:" << treasure << endl;
isOpen = false;
}
}
// 查看宝物
void look(){
if (isOpen) {
cout << "你看到宝箱里有:" << treasure << endl;
} else {
cout << "宝箱是关着的" << endl;
}
}
};
int main(){
cout << "=== 魔法宝箱游戏 ===" << endl;
// 创建多个宝箱
{
cout << "\n--- 第一个宝箱区域 ---" << endl;
MagicBox box1;
box1.look();
cout << "--- 第一个宝箱区域结束 ---" << endl;
}
{
cout << "\n--- 第二个宝箱区域 ---" << endl;
MagicBox box2;
box2.look();
cout << "--- 第二个宝箱区域结束 ---" << endl;
}
cout << "\n=== 游戏结束 ===" << endl;
return 0;
}
扩展挑战
- 修改代码,让宝箱可以指定大小(小/中/大)
- 不同大小的宝箱有不同概率获得更好的宝物
- 添加一个
close()函数,可以手动关闭宝箱
🎮 趣味练习三:游戏角色管理系统
项目描述
创建一个游戏角色管理系统,每个角色都有构造函数和析构函数,管理角色的创建和销毁。
项目要求
- 每个角色有名字、职业、等级、生命值
- 创建角色时(构造函数)显示欢迎信息
- 角色升级时,生命值增加
- 角色销毁时(析构函数)显示告别信息
代码框架
#include <iostream>
#include <string>
using namespace std;
class GameCharacter {
private:
string name;
string job;
int level;
int health;
public:
// 构造函数
GameCharacter(string n, string j) {
name = n;
job = j;
level = 1;
health = 100;
cout << "欢迎" << name << "(" << job << ")加入游戏!" << endl;
}
// 析构函数
~GameCharacter() {
cout << name << "离开了游戏..." << endl;
}
// 升级
void levelUp(){
level++;
health += 20;
cout << name << "升级了!现在是" << level << "级,生命值" << health << endl;
}
// 显示信息
void showInfo(){
cout << "===== 角色信息 =====" << endl;
cout << "名字:" << name << endl;
cout << "职业:" << job << endl;
cout << "等级:" << level << endl;
cout << "生命值:" << health << endl;
cout << "===================" << endl;
}
};
int main(){
cout << "=== 游戏角色管理系统 ===" << endl;
// 创建角色
GameCharacter hero1("小明", "战士");
GameCharacter hero2("小红", "法师");
// 显示信息
hero1.showInfo();
hero2.showInfo();
// 升级
hero1.levelUp();
hero2.levelUp();
hero2.levelUp();
cout << "=== 游戏结束 ===" << endl;
return 0;
}
❓ 常见问题解答
Q1:什么时候需要写析构函数?
A: 当你的类中使用了动态内存(new)、打开了文件、建立了网络连接等需要手动清理的资源时,就需要写析构函数来释放这些资源。
Q2:构造函数可以重载吗?
A: 可以!就像普通函数一样,构造函数也可以重载。一个类可以有多个构造函数,只要参数不同就可以。
Q3:析构函数可以手动调用吗?
A: 通常不建议手动调用析构函数。析构函数会在对象销毁时自动调用。手动调用可能会导致重复清理,引发错误。
Q4:构造函数和析构函数可以是私有的吗?
A: 可以,但这样做的场景比较特殊。比如单例模式(只能创建一个对象的设计模式)就会把构造函数设为私有。
Q5:如果没有写构造函数和析构函数会怎样?
A: 编译器会自动生成一个默认的构造函数和析构函数。默认的构造函数什么也不做,默认的析构函数也什么也不做。
💪 编程挑战:学生成绩管理系统V4.0
挑战任务
基于之前的学生成绩管理系统,使用构造函数和析构函数来改进:
- 为
Student类添加构造函数,创建学生时自动初始化信息
- 为
Student类添加析构函数,学生“毕业”时显示信息
- 为
Course类添加构造函数和析构函数
- 创建
ClassRoom类,管理多个学生
挑战要点
- 使用构造函数初始化学生信息
- 使用析构函数清理资源
- 观察构造函数和析构函数的调用顺序
- 尝试使用动态内存分配(
new/delete)
📖 下期预告:继承——动物的“家族树”
在下期教程中,我们将学习面向对象的另一个重要概念——继承。
你将学到:
- 什么是继承?为什么需要继承?
- 如何创建派生类?
- 基类和派生类的关系
- 继承中的访问控制
- 实战项目:动物家族管理系统
生活比喻:
- 基类:动物(有名字、年龄、会吃、会睡)
- 派生类:猫(会喵喵叫)、狗(会汪汪叫)、鸟(会飞)
🎉 总结
本期重点回顾
- 构造函数:对象的“出生仪式”,在创建对象时自动调用
- 析构函数:对象的“善后工作”,在销毁对象时自动调用
- 构造函数的特点:与类同名、无返回值、可重载
- 析构函数的特点:在类名前加
~、无返回值、无参数
- 实际应用:初始化对象、清理资源、管理生命周期
一句话记住:
“构造函数负责‘迎接’对象到来,析构函数负责‘送别’对象离开。”