什么是深度拷贝?
深度拷贝:创建一个新对象,并递归地复制原对象的所有成员(包括指针指向的内容),使得两个对象完全独立。
浅拷贝:只复制指针的值(地址),不复制指针指向的内容,导致两个对象共享同一块内存。
1. 基本的深度拷贝实现
自定义String类的深度拷贝示例
cpp
#include <iostream>
#include <cstring>
class MyString {
private:
char* m_data; // 指向字符串的指针
int m_length; // 字符串长度
public:
// 构造函数
MyString(const char* str = "") {
std::cout << "构造函数被调用" << std::endl;
m_length = strlen(str); // 计算字符串长度
m_data = new char[m_length + 1]; // 动态分配内存(+1用于存放'\0')
strcpy(m_data, str); // 复制字符串内容
}
// 拷贝构造函数(深度拷贝)
MyString(const MyString& other) {
std::cout << "拷贝构造函数被调用(深度拷贝)" << std::endl;
m_length = other.m_length; // 复制长度
m_data = new char[m_length + 1]; // 重新分配内存
strcpy(m_data, other.m_data); // 复制字符串内容
}
// 拷贝赋值运算符(深度拷贝)
MyString& operator=(const MyString& other) {
std::cout << "拷贝赋值运算符被调用(深度拷贝)" << std::endl;
if (this != &other) { // 防止自赋值
delete[] m_data; // 释放原有内存
m_length = other.m_length; // 复制长度
m_data = new char[m_length + 1]; // 重新分配内存
strcpy(m_data, other.m_data); // 复制字符串内容
}
return *this; // 返回当前对象的引用
}
// 析构函数
~MyString() {
std::cout << "析构函数被调用,释放内存: " << (void*)m_data << std::endl;
delete[] m_data; // 释放动态分配的内存
}
// 修改字符串内容
void setString(const char* str) {
delete[] m_data; // 释放旧内存
m_length = strlen(str); // 计算新长度
m_data = new char[m_length + 1]; // 分配新内存
strcpy(m_data, str); // 复制新内容
}
// 获取字符串
const char* getString() const {
return m_data;
}
// 打印字符串地址和内容
void printInfo(const char* name) const {
std::cout << name << " - 地址: " << (void*)m_data
<< ", 内容: \"" << m_data << "\"" << std::endl;
}
};
// 演示函数:展示拷贝行为
void demonstrateShallowCopyProblem() {
std::cout << "=== 如果没有深度拷贝会发生什么 ===" << std::endl;
// 假设我们使用浅拷贝(编译器默认生成的)
// MyString str1("Hello");
// MyString str2 = str1; // 浅拷贝:两个对象指向同一块内存
// 当str1修改内容时,str2也会受影响(非预期行为)
// 当str1和str2析构时,同一块内存会被释放两次(程序崩溃)
}
int main() {
std::cout << "=== 深度拷贝演示 ===" << std::endl;
// 创建原始对象
MyString original("Hello World");
original.printInfo("original");
std::cout << "\n--- 使用拷贝构造函数 ---" << std::endl;
// 调用拷贝构造函数(深度拷贝)
MyString copy1(original);
copy1.printInfo("copy1");
std::cout << "\n--- 使用拷贝赋值运算符 ---" << std::endl;
// 调用拷贝赋值运算符(深度拷贝)
MyString copy2("Temp");
copy2 = original;
copy2.printInfo("copy2");
std::cout << "\n--- 修改原始对象验证独立性 ---" << std::endl;
original.setString("Modified");
original.printInfo("original");
copy1.printInfo("copy1"); // copy1不受影响
copy2.printInfo("copy2"); // copy2不受影响
std::cout << "\n--- 作用域结束,自动调用析构函数 ---" << std::endl;
// 所有对象离开作用域时,各自的析构函数会被调用
// 每个对象释放自己独立的内存,不会出现重复释放
return 0;
}
2. 复杂对象的深度拷贝
包含嵌套对象的深度拷贝
cpp
#include <iostream>
#include <cstring>
class Person {
private:
char* m_name;
int m_age;
public:
// 构造函数
Person(const char* name, int age) : m_age(age) {
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
std::cout << "Person构造函数: " << m_name << std::endl;
}
// 拷贝构造函数(深度拷贝)
Person(const Person& other) : m_age(other.m_age) {
m_name = new char[strlen(other.m_name) + 1];
strcpy(m_name, other.m_name);
std::cout << "Person拷贝构造函数: " << m_name << std::endl;
}
// 拷贝赋值运算符(深度拷贝)
Person& operator=(const Person& other) {
if (this != &other) {
delete[] m_name;
m_age = other.m_age;
m_name = new char[strlen(other.m_name) + 1];
strcpy(m_name, other.m_name);
}
std::cout << "Person拷贝赋值: " << m_name << std::endl;
return *this;
}
// 析构函数
~Person() {
std::cout << "Person析构函数: " << m_name << std::endl;
delete[] m_name;
}
void setName(const char* name) {
delete[] m_name;
m_name = new char[strlen(name) + 1];
strcpy(m_name, name);
}
void printInfo() const {
std::cout << "姓名: " << m_name << ", 年龄: " << m_age << std::endl;
}
};
class Department {
private:
char* m_name;
Person* m_manager; // 指向Person对象的指针
public:
// 构造函数
Department(const char* deptName, const Person& manager) {
m_name = new char[strlen(deptName) + 1];
strcpy(m_name, deptName);
// 深度拷贝:为新部门创建新的经理对象
m_manager = new Person(manager);
std::cout << "Department构造函数: " << m_name << std::endl;
}
// 拷贝构造函数(深度拷贝)
Department(const Department& other) {
// 复制部门名称
m_name = new char[strlen(other.m_name) + 1];
strcpy(m_name, other.m_name);
// 深度拷贝经理对象
m_manager = new Person(*(other.m_manager));
std::cout << "Department拷贝构造函数: " << m_name << std::endl;
}
// 拷贝赋值运算符(深度拷贝)
Department& operator=(const Department& other) {
if (this != &other) {
// 释放原有资源
delete[] m_name;
delete m_manager;
// 复制新资源
m_name = new char[strlen(other.m_name) + 1];
strcpy(m_name, other.m_name);
m_manager = new Person(*(other.m_manager));
}
std::cout << "Department拷贝赋值: " << m_name << std::endl;
return *this;
}
// 析构函数
~Department() {
std::cout << "Department析构函数: " << m_name << std::endl;
delete[] m_name;
delete m_manager; // 释放经理对象
}
void printInfo() const {
std::cout << "部门: " << m_name << ", 经理: ";
m_manager->printInfo();
}
};
void demonstrateComplexDeepCopy() {
std::cout << "=== 复杂对象深度拷贝演示 ===" << std::endl;
// 创建原始对象
Person manager1("张三", 35);
Department dept1("技术部", manager1);
std::cout << "\n--- 拷贝部门对象 ---" << std::endl;
// 深度拷贝:会递归拷贝所有成员,包括指向的对象
Department dept2 = dept1;
std::cout << "\n--- 修改原始部门信息 ---" << std::endl;
// 修改原始部门的经理
Person newManager("李四", 40);
Department dept3("销售部", newManager);
dept1 = dept3; // 拷贝赋值
std::cout << "\n--- 显示各部门信息 ---" << std::endl;
dept1.printInfo();
dept2.printInfo(); // dept2不受dept1修改的影响
dept3.printInfo();
std::cout << "\n--- 离开函数作用域 ---" << std::endl;
// 所有对象都会正确释放自己的资源
}
int main() {
demonstrateComplexDeepCopy();
std::cout << "\n=== 程序结束 ===" << std::endl;
return 0;
}
3. 现代C++的深度拷贝最佳实践
使用智能指针避免手动内存管理
cpp
#include <iostream>
#include <memory>
#include <vector>
#include <cstring>
class ModernString {
private:
std::unique_ptr<char[]> m_data; // 使用智能指针自动管理内存
int m_length;
public:
// 构造函数
ModernString(const char* str = "") : m_length(strlen(str)) {
m_data = std::make_unique<char[]>(m_length + 1);
strcpy(m_data.get(), str);
std::cout << "ModernString构造函数: " << m_data.get() << std::endl;
}
// 拷贝构造函数(深度拷贝)
ModernString(const ModernString& other) : m_length(other.m_length) {
m_data = std::make_unique<char[]>(m_length + 1);
strcpy(m_data.get(), other.m_data.get());
std::cout << "ModernString拷贝构造函数: " << m_data.get() << std::endl;
}
// 拷贝赋值运算符(深度拷贝)
ModernString& operator=(const ModernString& other) {
if (this != &other) {
m_length = other.m_length;
m_data = std::make_unique<char[]>(m_length + 1);
strcpy(m_data.get(), other.m_data.get());
}
std::cout << "ModernString拷贝赋值: " << m_data.get() << std::endl;
return *this;
}
// 移动构造函数(C++11)
ModernString(ModernString&& other) noexcept
: m_data(std::move(other.m_data)), m_length(other.m_length) {
other.m_length = 0;
std::cout << "ModernString移动构造函数" << std::endl;
}
// 移动赋值运算符(C++11)
ModernString& operator=(ModernString&& other) noexcept {
if (this != &other) {
m_data = std::move(other.m_data);
m_length = other.m_length;
other.m_length = 0;
}
std::cout << "ModernString移动赋值" << std::endl;
return *this;
}
// 注意:不需要显式定义析构函数!
// 智能指针会自动管理内存释放
const char* get() const {
return m_data.get();
}
void printInfo(const char* name) const {
std::cout << name << ": " << m_data.get() << std::endl;
}
};
void demonstrateModernDeepCopy() {
std::cout << "=== 现代C++深度拷贝演示 ===" << std::endl;
ModernString str1("Hello Modern C++");
ModernString str2 = str1; // 深度拷贝
ModernString str3;
str3 = str1; // 深度拷贝赋值
str1.printInfo("str1");
str2.printInfo("str2");
str3.printInfo("str3");
// 使用移动语义(避免不必要的拷贝)
std::cout << "\n--- 移动语义演示 ---" << std::endl;
ModernString str4 = std::move(str1); // 移动构造
str4.printInfo("str4");
// str1现在处于有效但未指定状态
std::cout << "\n--- 离开作用域,自动释放资源 ---" << std::endl;
}
int main() {
demonstrateModernDeepCopy();
return 0;
}
深度拷贝的关键要点总结
-
何时需要深度拷贝:
-
类包含原始指针成员
-
指针指向动态分配的内存
-
需要对象的完全独立性
-
-
必须实现的函数(Rule of Three):
-
拷贝构造函数
-
拷贝赋值运算符
-
析构函数
-
-
现代C++改进(Rule of Five):
-
增加移动构造函数
-
增加移动赋值运算符
-
使用智能指针简化内存管理
-
-
深度拷贝的步骤:
-
分配新内存
-
复制数据内容
-
在赋值运算符中处理自赋值情况
-
在析构函数中正确释放资源
-
通过深度拷贝,可以确保对象的完全独立性,避免悬空指针、内存泄漏和重复释放等问题。