理解C++对象的生命周期对于编写高效、安全的代码至关重要。对象的生命周期包括创建、使用和销毁三个阶段,每个阶段都有特定的规则和机制。本章将详细介绍对象的构造、析构、复制、移动以及生命周期的管理。
构造函数是对象创建时自动调用的特殊成员函数,用于初始化对象的状态。
默认构造函数
#include <iostream> 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; } };
析构函数在对象销毁时自动调用,用于清理资源。
#include <iostream> #include <fstream> 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; }
初始化列表是初始化成员变量的首选方式,特别是对于const成员和引用成员。
#include <iostream> 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成员 | 可以初始化 | 不能初始化 |
| 引用成员 | 可以初始化 | 不能初始化 |
| 效率 | 直接构造 | 默认构造+赋值 |
| 成员类构造函数选择 | 可以指定 | 只能默认构造 |
#include <iostream> #include <vector> using namespace std; class ModernClass { private: int x = 10; // 类内初始值 string name = "default"; // 类内初始值 vector<int> 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; }
拷贝构造函数用于创建一个新对象作为现有对象的副本。
#include <iostream> #include <cstring> 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; // 禁止赋值 };
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; } };
移动语义允许将资源从一个对象“移动”到另一个对象,而不是复制,从而提高性能。
#include <iostream> #include <vector> using namespace std; class HugeData { vector<int> *data; public: HugeData() { data = new vector<int>(1000000); cout << "构造函数" << endl; } // 拷贝构造函数 - 深拷贝,代价高 HugeData(const HugeData &other) { data = new vector<int>(*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; }
class HugeData { vector<int> *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处于有效但未指定状态 }
#include <iostream> #include <string> using namespace std; void process(int &x) { cout << "左值引用: " << x << endl; } void process(int &&x) { cout << "右值引用: " << x << endl; } void forward(int &&x) { // 完美转发 process(std::forward<int>(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; }
#include <iostream> 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 析构
#include <iostream> 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; }
#include <iostream> 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; }
#include <iostream> using namespace std; // 简化版的unique_ptr template <typename T> 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<int> p1(new int(10)); cout << *p1 << endl; UniquePtr<int> p2 = move(p1); // p1现在是空指针 return 0; }
#include <iostream> #include <cstring> #include <utility> 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; }
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<T>: - 预分配一定数量的对象 - 提供acquire方法获取对象 - 提供release方法归还对象 - 使用RAII确保资源安全
8. 完整的字符串类
基于本章的SafeString,添加以下功能: - 比较运算符(==, !=, <, >) - 拼接运算符(+) - 查找子串 - 获取子串 - 插入和删除
9. 生命周期追踪器
创建一个ObjectTracker类,能够: - 统计当前存活的对象数量 - 追踪对象的构造和析构 - 输出对象生命周期报告
10. 深度分析
本章详细学习了C++对象的生命周期管理:
- 构造函数 负责对象的初始化,支持重载和委托 - 析构函数 负责对象的清理,遵循栈的后进先出原则 - 初始化列表 是初始化成员的首选方式,对于const和引用成员是必须的 - 拷贝控制 包括拷贝构造函数和拷贝赋值运算符,需要注意深拷贝 - 移动语义 (C++11)允许高效地转移资源所有权 - RAII 是资源管理的核心原则,确保资源的安全获取和释放
理解对象的生命周期对于编写高效、安全的C++代码至关重要,特别是在涉及资源管理和异常处理的场景下。