当然可以!以下是《类和对象(3)——构造函数、析构函数和拷贝构造函数》的详尽讲解,适合 C++ 初学者夯实面向对象基础,也是掌握内存管理与对象生命周期的关键一课。
🎯 类和对象(3) —— 构造函数、析构函数和拷贝构造函数详解
📘《Hello C++ World!》系列第5篇
📚 目录
- 构造函数:对象诞生的仪式
- 析构函数:对象终结的告别
- 拷贝构造函数:复制的本质
- 构造函数重载与默认构造
- 编译器自动生成行为
- 深拷贝 vs 浅拷贝
- const 对象与引用参数的构造问题
- 实战演示:动态数组类构建与析构追踪
- 总结与建议
1️⃣ 构造函数(Constructor)
构造函数是在 对象创建时自动调用的特殊函数,用于初始化对象成员变量。
✨ 语法特点:
class Person {
public:
std::string name;
int age;
// 构造函数
Person(std::string n, int a) {
name = n;
age = a;
}
};
✨ 特性:
- 无需显式调用,
Person p("Tom", 18);
自动触发 - 没有返回值类型
- 支持重载(多个构造函数)
2️⃣ 析构函数(Destructor)
析构函数在 对象生命周期结束时自动调用,用于释放资源、打印日志等清理工作。
✨ 语法特点:
~Person() {
std::cout << name << " 被析构了\n";
}
- 名称前带
~
- 无参数、无返回值
- 一个类只能有一个析构函数
3️⃣ 拷贝构造函数(Copy Constructor)
当你使用一个对象去初始化另一个对象时,会触发拷贝构造函数:
Person p1("Alice", 20);
Person p2 = p1; // 拷贝构造调用
✨ 定义形式:
Person(const Person& other) {
name = other.name;
age = other.age;
std::cout << "调用拷贝构造\n";
}
✨ 典型调用场景:
- 以值传参
- 函数返回值(返回对象)
- 显式或隐式拷贝初始化
4️⃣ 构造函数重载与默认构造函数
✅ 重载构造函数:
Person() { name = "Unknown"; age = 0; }
Person(std::string n) { name = n; age = 0; }
Person(std::string n, int a) { name = n; age = a; }
✅ 默认构造函数:
无参构造函数被称为默认构造:
Person(); // 默认构造
如果你手动定义了任何带参构造函数,编译器不会自动生成默认构造函数,需要显式声明:
Person() = default;
5️⃣ 编译器的自动生成规则
如未手动定义,编译器会提供:
函数类型 | 是否自动生成 |
---|---|
默认构造函数 | ✅(无其他构造时) |
拷贝构造函数 | ✅ |
拷贝赋值运算符 | ✅ |
析构函数 | ✅ |
但一旦手写其中之一,其他的可能不会再自动生成,必须显式声明。
6️⃣ 深拷贝 vs 浅拷贝
✅ 浅拷贝(默认行为):
只是复制指针,不复制数据所指向的内容。
class MyArray {
int* data;
public:
MyArray() { data = new int[10]; }
~MyArray() { delete[] data; }
};
默认拷贝构造函数只复制 data
指针,会导致 双重释放 (double free)。
✅ 深拷贝(手写拷贝构造):
MyArray(const MyArray& other) {
data = new int[10];
std::copy(other.data, other.data + 10, data);
}
7️⃣ const 对象与引用参数的构造限制
void show(const Person& p); // 推荐:防止拷贝,支持常量
void show(Person p); // 会触发拷贝构造
const Person p; // 需要默认构造函数 + 支持 const 限定的成员访问
8️⃣ 实战演示:对象构造与析构过程
class Tracker {
std::string id;
public:
Tracker(std::string s) : id(s) {
std::cout << "构造: " << id << "\n";
}
Tracker(const Tracker& other) : id(other.id + "-copy") {
std::cout << "拷贝构造: " << id << "\n";
}
~Tracker() {
std::cout << "析构: " << id << "\n";
}
};
int main() {
Tracker t1("A");
Tracker t2 = t1; // 拷贝构造
return 0; // 析构顺序:t2 -> t1
}
✅ 总结与建议
特性 | 构造函数 | 析构函数 | 拷贝构造 |
---|---|---|---|
是否可重载 | ✅ | ❌ | ❌ |
调用时机 | 创建对象 | 释放对象 | 拷贝对象 |
是否自动生成 | ✅ | ✅ | ✅ |
是否需自定义 | ⚠️ 资源管理类建议手写 | ⚠️ 删除资源的类必须写 | ⚠️ 存在指针成员时必须写 |
🎯 建议:
- 对于有指针资源的类:必须写 三大函数(构造、拷贝构造、析构)
- 养成
Rule of Three
(甚至 Rule of Five)思想 - 拷贝构造必须使用
const 引用
- 析构函数必须是
virtual
(若用于继承)
📚 参考资料
- cppreference – Constructors
- [Effective C++ – Scott Meyers]
- [C++ Primer 第5版]
- 《More Effective C++》构造与析构细节专题
如需我补充该内容为图文笔记、教学 PPT、或配套项目例程(如模拟 String 类、资源管理类),欢迎告诉我!是否需要接下来继续讲解赋值运算符重载与移动构造函数?
发表回复