C++ 拷贝构造函数和重载赋值运算符
一、为什么需要拷贝构造函数和重载拷贝运算符?先了解深拷贝和浅拷贝。
在 C++ 中,深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种完全不同的对象拷贝方式,它们的核心区别在于如何处理指针或动态分配的资源。以下是详细对比:
1. 浅拷贝(Shallow Copy)
定义
-
逐字节复制:直接复制对象的所有成员变量的值(包括指针地址)。
-
不创建新资源:拷贝后的对象和原对象共享同一块动态内存或资源。
class String { public:char* data; // 动态分配的字符数组String(const char* str = "") {data = new char[strlen(str) + 1];strcpy(data, str);}// 默认拷贝构造函数是浅拷贝! };int main() {String s1("Hello");String s2 = s1; // 浅拷贝:s2.data 和 s1.data 指向同一内存s2.data[0] = 'X'; // 修改 s2 会影响 s1!std::cout << s1.data; // 输出 "Xello"(本不应被修改) }
问题
-
双重释放:两个对象析构时会尝试释放同一块内存,导致崩溃。
-
数据意外共享:一个对象的修改会影响另一个对象。
2. 深拷贝(Deep Copy)
-
定义
-
创建新资源:为新对象独立分配内存,并复制原对象指向的实际数据。
-
完全独立:拷贝后的对象和原对象不共享任何资源。
实现方式
---> 需自定义拷贝构造函数和赋值运算符:
class String {
public:char* data;// 深拷贝构造函数String(const String& other) {data = new char[strlen(other.data) + 1]; // 新分配内存strcpy(data, other.data); // 复制数据}// 深拷贝赋值运算符String& operator=(const String& other) {if (this != &other) { // 自赋值检查delete[] data; // 释放旧资源data = new char[strlen(other.data) + 1];strcpy(data, other.data);}return *this;}~String() { delete[] data; }
};int main() {String s1("Hello");String s2 = s1; // 深拷贝:s2.data 是新分配的内存s2.data[0] = 'X'; // 修改 s2 不会影响 s1std::cout << s1.data; // 输出 "Hello"(保持原样)
}
优点
-
避免资源冲突:每个对象拥有独立资源。
-
安全析构:不会双重释放内存。
3. 关键对比
特性 | 浅拷贝 | 深拷贝 |
---|---|---|
拷贝内容 | 复制指针地址(共享资源) | 复制指针指向的实际数据(独立资源) |
资源管理 | 危险(需外部协调释放) | 安全(每个对象管理自己的资源) |
默认行为 | C++ 默认提供 | 需手动实现 |
性能 | 快(仅复制指针) | 慢(需分配内存+复制数据) |
典型问题 | 双重释放、数据竞争 | 无 |
适用场景 | 仅限无资源的简单类 | 管理资源的类 |
二、拷贝构造函数 与 赋值运算符
1、拷贝构造函数
-
在以下情况被调用:
String s1("Hello"); String s2 = s1; // 拷贝构造(初始化) String s3(s1); // 拷贝构造
用于对象的初始化(从另一个对象创建新对象)
2.重载赋值运算符
在以下情况被调用:
String s1("Hello");
String s2;
s2 = s1; // 拷贝赋值(已存在对象被覆盖)
用于已存在对象的赋值(覆盖原有值)。
示例:仅实现拷贝构造的缺陷
class String {char* data;
public:String(const char* str) { /* 分配内存 */ }String(const String& other) { /* 深拷贝 */ } // 实现了拷贝构造~String() { delete[] data; }// 未实现 operator=
};int main() {String s1("Hello");String s2("World");s2 = s1; // 使用默认的 operator=,浅拷贝!// s2 的原始内存泄漏,且 s1 和 s2 指向同一内存
}
结果:程序崩溃(双重释放)或内存泄漏。
补充:在C++中,将 拷贝构造函数(Copy Constructor) 和 赋值运算符(Copy Assignment Operator) 声明为 private
(私有)的主要作用是 禁止类的对象被拷贝或赋值。这是C++中一种常用的设计技巧,通常用于控制对象的复制行为。
在C++中,private
成员(包括构造函数)只能被类的成员函数或友元访问。