====== 第十章 对象生命周期 ====== 理解C++对象的生命周期对于编写高效、安全的代码至关重要。对象的生命周期包括创建、使用和销毁三个阶段,每个阶段都有特定的规则和机制。本章将详细介绍对象的构造、析构、复制、移动以及生命周期的管理。 ===== 10.1 对象的创建与销毁 ===== ==== 10.1.1 构造函数详解 ==== 构造函数是对象创建时自动调用的特殊成员函数,用于初始化对象的状态。 **默认构造函数** #include using namespace std; class Person { private: string name; int age; public: // 默认构造函数(无参) Person() { name = "Unknown"; age = 0; cout << "默认构造函数被调用" << endl; } void display() const { cout << name << ", " << age << "岁" << endl; } }; int main() { Person p1; // 调用默认构造函数 Person p2(); // 注意:这是函数声明,不是创建对象! Person p3{}; // C++11统一初始化,调用默认构造函数 p1.display(); return 0; } **带参数的构造函数** class Person { private: string name; int age; public: Person(const string &n, int a) { name = n; age = a; cout << "带参数构造函数: " << name << endl; } // 使用初始化列表(更高效) Person(const string &n, int a, bool useInitList) : name(n), age(a) { cout << "初始化列表构造: " << name << endl; } }; **委托构造函数(C++11)** class Person { private: string name; int age; string address; public: // 主构造函数 Person(const string &n, int a, const string &addr) : name(n), age(a), address(addr) { cout << "主构造函数" << endl; } // 委托构造函数 Person(const string &n, int a) : Person(n, a, "Unknown") { cout << "委托构造函数(2参数)" << endl; } Person() : Person("Unknown", 0) { cout << "委托构造函数(无参)" << endl; } }; ==== 10.1.2 析构函数详解 ==== 析构函数在对象销毁时自动调用,用于清理资源。 #include #include using namespace std; class FileLogger { private: ofstream file; string name; public: FileLogger(const string &filename) : name(filename), file(filename) { cout << "打开日志文件: " << filename << endl; file << "=== 日志开始 ===" << endl; } void log(const string &message) { file << message << endl; } // 析构函数 ~FileLogger() { file << "=== 日志结束 ===" << endl; cout << "关闭日志文件: " << name << endl; // file会自动关闭 } }; void useLogger() { FileLogger logger("app.log"); logger.log("程序启动"); logger.log("执行操作"); // 函数结束时,logger的析构函数自动调用 } int main() { useLogger(); cout << "logger已销毁" << endl; return 0; } ===== 10.2 成员初始化 ===== ==== 10.2.1 初始化列表 ==== 初始化列表是初始化成员变量的首选方式,特别是对于const成员和引用成员。 #include using namespace std; class Example { private: const int constValue; // const成员必须在初始化列表中初始化 int &ref; // 引用成员必须在初始化列表中初始化 int normalValue; string name; public: // 使用初始化列表 Example(int cv, int &r, int nv, const string &n) : constValue(cv), // 初始化const成员 ref(r), // 初始化引用成员 normalValue(nv), // 初始化普通成员 name(n) { // 初始化string cout << "构造函数体执行" << endl; // 这里不再是初始化,而是赋值 } }; class Member { public: Member() { cout << "Member默认构造" << endl; } Member(int) { cout << "Member(int)构造" << endl; } }; class Container { Member m1; Member m2; public: // 在初始化列表中指定使用的构造函数 Container() : m1(), m2(10) { cout << "Container构造" << endl; } }; **初始化列表 vs 构造函数体内赋值** | 特性 | 初始化列表 | 构造函数体内赋值 | |------|------------|------------------| | const成员 | 可以初始化 | 不能初始化 | | 引用成员 | 可以初始化 | 不能初始化 | | 效率 | 直接构造 | 默认构造+赋值 | | 成员类构造函数选择 | 可以指定 | 只能默认构造 | ==== 10.2.2 类内成员初始化(C++11) ==== #include #include using namespace std; class ModernClass { private: int x = 10; // 类内初始值 string name = "default"; // 类内初始值 vector data = {1, 2, 3}; // 类内初始值 static int count; // 静态成员 public: ModernClass() = default; // 显式默认构造函数 ModernClass(int xVal) : x(xVal) { // x使用传入的值,name和data使用类内初始值 } void print() const { cout << "x=" << x << ", name=" << name << endl; cout << "data: "; for (int v : data) cout << v << " "; cout << endl; } }; int ModernClass::count = 0; // 静态成员定义 int main() { ModernClass obj1; // 使用类内初始值 ModernClass obj2(100); // x=100,其他使用类内初始值 obj1.print(); obj2.print(); return 0; } ===== 10.3 拷贝控制 ===== ==== 10.3.1 拷贝构造函数 ==== 拷贝构造函数用于创建一个新对象作为现有对象的副本。 #include #include using namespace std; class String { private: char *data; int len; public: // 普通构造函数 String(const char *str = "") { len = strlen(str); data = new char[len + 1]; strcpy(data, str); cout << "构造函数: " << data << endl; } // 拷贝构造函数 String(const String &other) { len = other.len; data = new char[len + 1]; strcpy(data, other.data); cout << "拷贝构造函数: " << data << endl; } // 析构函数 ~String() { cout << "析构函数: " << (data ? data : "null") << endl; delete[] data; } void print() const { cout << data << endl; } }; // 拷贝构造函数被调用的情况 void demo() { String s1("Hello"); // 情况1: 使用另一个对象初始化 String s2 = s1; // 拷贝构造 // 情况2: 作为参数传递给值传递的函数 void func(String s); // 调用时会拷贝 // 情况3: 作为返回值(可能被优化) String create() { return String("World"); } } // 禁用拷贝 class NonCopyable { public: NonCopyable() = default; NonCopyable(const NonCopyable &) = delete; // 禁止拷贝 NonCopyable &operator=(const NonCopyable &) = delete; // 禁止赋值 }; ==== 10.3.2 拷贝赋值运算符 ==== class String { // ... 前面的代码 ... public: // 拷贝赋值运算符 String &operator=(const String &other) { cout << "拷贝赋值运算符" << endl; // 1. 检查自赋值 if (this != &other) { // 2. 分配新资源 char *newData = new char[other.len + 1]; strcpy(newData, other.data); // 3. 释放旧资源 delete[] data; // 4. 指向新资源 data = newData; len = other.len; } return *this; } // 更安全的写法:拷贝并交换惯用法 String &operator=(String other) { cout << "拷贝并交换赋值运算符" << endl; swap(data, other.data); swap(len, other.len); return *this; } }; ===== 10.4 移动语义(C++11) ===== ==== 10.4.1 为什么需要移动语义 ==== 移动语义允许将资源从一个对象"移动"到另一个对象,而不是复制,从而提高性能。 #include #include using namespace std; class HugeData { vector *data; public: HugeData() { data = new vector(1000000); cout << "构造函数" << endl; } // 拷贝构造函数 - 深拷贝,代价高 HugeData(const HugeData &other) { data = new vector(*other.data); cout << "拷贝构造函数(深拷贝)" << endl; } // 移动构造函数 - 转移所有权,代价低 HugeData(HugeData &&other) noexcept { data = other.data; // 窃取资源 other.data = nullptr; // 将源对象置为空 cout << "移动构造函数(转移)" << endl; } ~HugeData() { delete data; cout << "析构函数" << endl; } }; HugeData createData() { HugeData temp; return temp; // 这里会调用移动构造函数(C++11起) } int main() { cout << "=== 拷贝 ===" << endl; HugeData d1; HugeData d2 = d1; // 拷贝构造 cout << "\n=== 移动 ===" << endl; HugeData d3 = createData(); // 移动构造 cout << "\n=== 显式移动 ===" << endl; HugeData d4 = std::move(d3); // 显式移动 return 0; } ==== 10.4.2 移动赋值运算符 ==== class HugeData { vector *data; public: // ... 其他成员函数 ... // 移动赋值运算符 HugeData &operator=(HugeData &&other) noexcept { cout << "移动赋值运算符" << endl; if (this != &other) { // 释放当前资源 delete data; // 窃取对方的资源 data = other.data; other.data = nullptr; } return *this; } }; // 使用示例 void useMove() { HugeData d1; HugeData d2; d2 = std::move(d1); // 移动赋值 // 此后d1处于有效但未指定状态 } ==== 10.4.3 右值引用和std::move ==== #include #include using namespace std; void process(int &x) { cout << "左值引用: " << x << endl; } void process(int &&x) { cout << "右值引用: " << x << endl; } void forward(int &&x) { // 完美转发 process(std::forward(x)); } int main() { int a = 10; process(a); // 左值,调用第一个 process(20); // 右值,调用第二个 process(std::move(a)); // 将左值转为右值引用 // std::move的本质 // 它只是类型转换,不移动任何东西! // 移动操作是由移动构造函数/赋值运算符完成的 string s1 = "Hello"; string s2 = std::move(s1); // s1的内容被移动到s2 // s1现在处于有效但未指定状态 cout << "s1: " << s1 << endl; // 可能是空字符串 cout << "s2: " << s2 << endl; // "Hello" return 0; } ===== 10.5 对象的生命周期管理 ===== ==== 10.5.1 对象的创建顺序 ==== #include using namespace std; class Member { int id; public: Member(int i) : id(i) { cout << "Member " << id << " 构造" << endl; } ~Member() { cout << "Member " << id << " 析构" << endl; } }; class Container { Member m1; Member m2; int x; public: Container() : m2(2), m1(1), x(10) { // 注意:初始化顺序由声明顺序决定,不是初始化列表顺序! cout << "Container 构造" << endl; } ~Container() { cout << "Container 析构" << endl; } }; int main() { Container c; return 0; } // 输出顺序: // Member 1 构造 // Member 2 构造 // Container 构造 // Container 析构 // Member 2 析构 // Member 1 析构 ==== 10.5.2 静态对象的生命周期 ==== #include using namespace std; class Singleton { private: static Singleton instance; int value; Singleton() : value(0) { cout << "Singleton 构造" << endl; } public: static Singleton &getInstance() { return instance; } void setValue(int v) { value = v; } int getValue() const { return value; } }; // 静态成员定义 Singleton Singleton::instance; class LocalStatic { public: LocalStatic() { cout << "LocalStatic 构造" << endl; } ~LocalStatic() { cout << "LocalStatic 析构" << endl; } }; void useLocalStatic() { // 局部静态对象,第一次调用时构造,程序结束时析构 static LocalStatic ls; cout << "使用局部静态对象" << endl; } int main() { cout << "main开始" << endl; Singleton &s = Singleton::getInstance(); s.setValue(100); useLocalStatic(); useLocalStatic(); // 不会再次构造 cout << "main结束" << endl; return 0; } ==== 10.5.3 动态对象的生命周期 ==== #include using namespace std; class Tracked { static int count; int id; public: Tracked() : id(++count) { cout << "对象 " << id << " 构造,当前总数: " << count << endl; } ~Tracked() { cout << "对象 " << id << " 析构,当前总数: " << --count << endl; } }; int Tracked::count = 0; void dynamicLifeDemo() { cout << "=== 栈对象 ===" << endl; Tracked t1; // 构造 { // 作用域开始 Tracked t2; // 构造 } // t2析构 cout << "\n=== 堆对象 ===" << endl; Tracked *p1 = new Tracked(); // 构造 Tracked *p2 = new Tracked[3]; // 构造3个 delete p1; // p1析构 delete[] p2; // 3个对象析构 } int main() { dynamicLifeDemo(); cout << "函数结束" << endl; return 0; } ===== 10.6 资源管理类(RAII) ===== ==== 10.6.1 智能指针的实现原理 ==== #include using namespace std; // 简化版的unique_ptr template class UniquePtr { private: T *ptr; public: explicit UniquePtr(T *p = nullptr) : ptr(p) {} // 禁止拷贝 UniquePtr(const UniquePtr &) = delete; UniquePtr &operator=(const UniquePtr &) = delete; // 允许移动 UniquePtr(UniquePtr &&other) noexcept : ptr(other.ptr) { other.ptr = nullptr; } UniquePtr &operator=(UniquePtr &&other) noexcept { if (this != &other) { delete ptr; ptr = other.ptr; other.ptr = nullptr; } return *this; } ~UniquePtr() { delete ptr; } T &operator*() const { return *ptr; } T *operator->() const { return ptr; } T *get() const { return ptr; } T *release() { T *temp = ptr; ptr = nullptr; return temp; } void reset(T *p = nullptr) { delete ptr; ptr = p; } }; int main() { UniquePtr p1(new int(10)); cout << *p1 << endl; UniquePtr p2 = move(p1); // p1现在是空指针 return 0; } ===== 10.7 综合示例 ===== ==== 实现一个安全的字符串类 ==== #include #include #include using namespace std; class SafeString { private: char *data; int length; int capacity; void ensureCapacity(int minCapacity) { if (minCapacity > capacity) { int newCapacity = max(minCapacity, capacity * 2); char *newData = new char[newCapacity + 1]; if (data) { strcpy(newData, data); } delete[] data; data = newData; capacity = newCapacity; } } public: // 默认构造函数 SafeString() : data(nullptr), length(0), capacity(0) {} // C字符串构造函数 SafeString(const char *str) { if (str) { length = strlen(str); capacity = length; data = new char[capacity + 1]; strcpy(data, str); } else { data = nullptr; length = capacity = 0; } } // 拷贝构造函数 SafeString(const SafeString &other) : length(other.length), capacity(other.capacity) { if (other.data) { data = new char[capacity + 1]; strcpy(data, other.data); } else { data = nullptr; } } // 移动构造函数 SafeString(SafeString &&other) noexcept : data(other.data), length(other.length), capacity(other.capacity) { other.data = nullptr; other.length = 0; other.capacity = 0; } // 析构函数 ~SafeString() { delete[] data; } // 拷贝赋值运算符 SafeString &operator=(const SafeString &other) { if (this != &other) { SafeString temp(other); // 拷贝构造临时对象 swap(temp.data, data); swap(temp.length, length); swap(temp.capacity, capacity); } return *this; } // 移动赋值运算符 SafeString &operator=(SafeString &&other) noexcept { if (this != &other) { delete[] data; data = other.data; length = other.length; capacity = other.capacity; other.data = nullptr; other.length = 0; other.capacity = 0; } return *this; } // 追加操作 SafeString &append(const char *str) { if (str) { int strLen = strlen(str); ensureCapacity(length + strLen); strcat(data + length, str); length += strLen; } return *this; } SafeString &append(const SafeString &other) { return append(other.data); } // 索引访问 char &operator[](int index) { return data[index]; } const char &operator[](int index) const { return data[index]; } // 获取C字符串 const char *c_str() const { return data ? data : ""; } int size() const { return length; } bool empty() const { return length == 0; } void print() const { cout << (data ? data : "(empty)") << endl; } }; int main() { SafeString s1("Hello"); SafeString s2 = s1; // 拷贝 SafeString s3; s3 = s1; // 赋值 s1.append(", World!"); s1.print(); // Hello, World! s2.print(); // Hello SafeString s4 = move(s1); // 移动 s4.print(); // Hello, World! return 0; } ===== 10.8 习题 ===== ==== 基础题 ==== 1. **构造函数练习** 创建一个Book类,包含书名、作者、价格。实现: - 默认构造函数 - 带参数的构造函数 - 委托构造函数 - 在构造函数中使用初始化列表 2. **拷贝控制** 分析以下代码的输出,解释为什么: class Test { int id; public: Test(int i) : id(i) { cout << "构造 " << id << endl; } Test(const Test &t) : id(t.id) { cout << "拷贝 " << id << endl; } ~Test() { cout << "析构 " << id << endl; } }; Test func() { Test t(1); return t; } int main() { Test t2 = func(); return 0; } 3. **成员初始化顺序** 编写程序验证成员初始化的顺序是由声明顺序决定的,而不是初始化列表中的顺序。 ==== 进阶题 ==== 4. **资源管理类** 实现一个FileHandle类,使用RAII模式管理文件资源: - 构造函数打开文件 - 析构函数关闭文件 - 禁止拷贝,允许移动 - 提供write和read方法 5. **深拷贝与浅拷贝** 创建两个类ShallowCopy和DeepCopy,分别包含一个动态分配的数组成员。演示: - 浅拷贝导致的问题 - 深拷贝如何解决问题 6. **移动语义应用** 实现一个Buffer类,管理一块动态内存。实现: - 拷贝构造函数(深拷贝) - 移动构造函数 - 拷贝赋值运算符 - 移动赋值运算符 比较拷贝和移动的性能差异。 ==== 综合题 ==== 7. **对象池实现** 设计一个对象池类ObjectPool: - 预分配一定数量的对象 - 提供acquire方法获取对象 - 提供release方法归还对象 - 使用RAII确保资源安全 8. **完整的字符串类** 基于本章的SafeString,添加以下功能: - 比较运算符(==, !=, <, >) - 拼接运算符(+) - 查找子串 - 获取子串 - 插入和删除 9. **生命周期追踪器** 创建一个ObjectTracker类,能够: - 统计当前存活的对象数量 - 追踪对象的构造和析构 - 输出对象生命周期报告 ==== 思考题 ==== 10. **深度分析** - 解释拷贝省略(Copy Elision)和返回值优化(RVO) - 讨论何时应该禁用拷贝,只允许移动 - 分析移动后对象的状态和使用限制 - 讨论异常安全性和强异常保证 ===== 10.9 小结 ===== 本章详细学习了C++对象的生命周期管理: - **构造函数** 负责对象的初始化,支持重载和委托 - **析构函数** 负责对象的清理,遵循栈的后进先出原则 - **初始化列表** 是初始化成员的首选方式,对于const和引用成员是必须的 - **拷贝控制** 包括拷贝构造函数和拷贝赋值运算符,需要注意深拷贝 - **移动语义** (C++11)允许高效地转移资源所有权 - **RAII** 是资源管理的核心原则,确保资源的安全获取和释放 理解对象的生命周期对于编写高效、安全的C++代码至关重要,特别是在涉及资源管理和异常处理的场景下。