在C++中,对象的拷贝可以通过浅拷贝(Shallow Copy)和深拷贝(Deep Copy)来实现。这两种拷贝方式在处理对象中的指针成员时有着根本的区别。
浅拷贝(Shallow Copy)
浅拷贝简单地复制一个对象的所有字段到另一个对象。如果对象中有指针成员,那么浅拷贝会复制指针的值,而不是指针指向的数据。这意味着,复制后的两个对象中的指针成员将指向内存中的同一个位置。因此,如果原始对象或副本修改了所指向的数据,另一个对象也会反映出这些修改。
举例
#include <iostream>
using namespace std;
class ShallowCopy {
public:
ShallowCopy(int _size) : size(_size) { data = new int[size]; }
int Get_Val() { return *data; }
int *Get_Val_add() { return data; }
void Set_Val(int value) { *data = value; } // 提供一个设置函数
ShallowCopy() {}
// 浅拷贝构造函数
ShallowCopy(const ShallowCopy& _sc) : size(_sc.size) { data = _sc.data; }
~ShallowCopy() { delete[] data; data = NULL; }
private:
int *data;
int size;
};
int main() {
ShallowCopy a(5);
ShallowCopy b = a; // 使用浅拷贝构造函数
b.Set_Val(10); // 修改b的数据
cout << a.Get_Val() << endl; // 输出10,因为a和b共享同一块内存
return 0;
}
在这个例子中,ShallowCopy
的拷贝构造函数实现了浅拷贝。当创建b
对象并复制a
时,b
的data
指针和a
的data
指向同一个内存地址。因此,修改b
的数据也会影响到a
。
深拷贝(Deep Copy)
深拷贝不仅仅复制对象的所有字段,而且会复制指针指向的数据。对于指针成员,深拷贝会在内存中为新对象分配新的空间,并将原始对象指针指向的数据复制到这块新空间中。因此,原始对象和副本对象将拥有各自独立的内存副本,修改一个对象不会影响到另一个对象。
举例
#include <iostream>
using namespace std;
class DeepCopy {
public:
DeepCopy(int _size) : size(_size) { data = new int[size]; }
int Get_Val() { return *data; }
int *Get_Val_add() { return data; }
void Set_Val(int value) { *data = value; } // 提供一个设置函数
DeepCopy() {}
// 深拷贝构造函数
DeepCopy(const DeepCopy& _dc) : size(_dc.size) {
data = new int[size];
for (int i = 0; i < size; ++i) {
data[i] = _dc.data[i];
}
}
~DeepCopy() { delete[] data; data = NULL; }
private:
int *data;
int size;
};
int main() {
DeepCopy a(5);
DeepCopy b = a; // 使用深拷贝构造函数
b.Set_Val(10); // 通过公共接口修改b的数据
cout << a.Get_Val() << endl; // 输出0,因为a和b有各自独立的内存副本
return 0;
}
在这个例子中,DeepCopy
的拷贝构造函数实现了深拷贝。当创建b
对象并复制a
时,b
的data
指针指向了一块新分配的内存,其内容是a
的data
指针指向数据的副本。因此,修改b
的数据不会影响到a
。
总结
- 浅拷贝速度快,但可能导致数据共享问题,特别是当对象包含指针成员时。
- 深拷贝会创建数据的完整副本,因此对象之间不会共享数据,但需要更多的内存和时间。
在实际应用中,应根据具体需求选择合适的拷贝方式。如果对象中包含指针成员,并且每个对象需要独立的数据副本,则应实现深拷贝。如果对象中没有指针成员,或者不需要独立的数据副本,浅拷贝通常是足够的。在设计类时,如果需要自定义拷贝行为,应该显式定义拷贝构造函数和拷贝赋值运算符,以防止默认的浅拷贝造成的问题。
为了展示如何重载复制运算符以实现深拷贝,我们将修改您提供的DeepCopy
类。在您的类中,我们已经有了一个深拷贝构造函数,但还没有重载复制运算符。我们将添加一个重载的复制运算符,以确保在对象赋值时进行深拷贝。
以下是修改后的例子:
#include <iostream>
using namespace std;
class DeepCopy {
public:
DeepCopy(int _size) : size(_size) { data = new int[size]; }
int Get_Val() { return *data; }
int *Get_Val_add() { return data; }
void Set_Val(int value) { *data = value; } // 提供一个设置函数
DeepCopy() {}
// 深拷贝构造函数
DeepCopy(const DeepCopy& _dc) : size(_dc.size) {
data = new int[size];
for (int i = 0; i < size; ++i) {
data[i] = _dc.data[i];
}
}
// 复制运算符重载
DeepCopy& operator=(const DeepCopy& other) {
if (this != &other) { // 防止自我赋值
delete[] data; // 释放旧资源
size = other.size; // 复制大小
data = new int[size]; // 分配新资源
for (int i = 0; i < size; ++i) {
data[i] = other.data[i]; // 复制数据
}
}
return *this; // 返回当前对象的引用
}
~DeepCopy() { delete[] data; data = NULL; }
private:
int *data;
int size;
};
int main() {
DeepCopy a(5);
DeepCopy b; // 默认构造函数
b = a; // 使用重载的复制运算符
b.Set_Val(10); // 通过公共接口修改b的数据
cout << a.Get_Val() << endl; // 输出随机值,因为a和b有各自独立的内存副本
cout << b.Get_Val() << endl; // 输出10,因为b的数据被修改了
return 0;
}
在这个修改后的例子中,我们添加了一个重载的复制运算符=
。这个运算符首先检查自我赋值的情况,然后释放当前对象指向的内存,分配新的内存,并复制other
对象的数据到当前对象。这样,无论何时一个DeepCopy
对象被赋值给另一个对象,都会进行深拷贝,确保两个对象拥有独立的内存副本。
在main
函数中,我们创建了两个DeepCopy
对象a
和b
。我们首先使用默认构造函数创建b
,然后使用重载的复制运算符=
将a
赋值给b
。随后,我们修改b
的数据并打印a
和b
的数据,可以看到它们各自拥有独立的内存副本。