====== 第十章 对象生命周期 ======
理解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++代码至关重要,特别是在涉及资源管理和异常处理的场景下。