概念
浅拷贝指的是复制对象的时候,对对象中的数据成员进行赋值操作,完成成员的复制,有点类似于函数中的值传递,系统的默认构造函数执行的也是浅拷贝。
而深拷贝是对对象中的成员除了赋值操作之外,还会重新动态申请内存空间来存储该数据。所以,如果我们没有重写拷贝构造函数时,系统会创建默认拷贝构造函数,此时的拷贝则都是浅拷贝。
浅拷贝的问题
我们大多数人写类的时候应该很少会去重写拷贝构造函数,这在类的数据成员中没有指针时,当然是可行的,但一旦数据成员中有指针,那么问题就出现了。此时调用浅拷贝(即系统创建的默认拷贝构造函数)会使新对象的指针指向的地址与被拷贝对象的指针指向的地址相同,即两者指向了同一段内存空间,那么当这两个对象被释放时就会调用两次析构函数去delete相同的内存空间而产生错误,所以此时就需要用到深拷贝了。
如何解决
深拷贝与浅拷贝的区别就在进行数据复制时会申请一块新的内存空间去存储数据,当遇到数据成员存在指针时,完美的避免了重复析构同一段内存空间的问题。当然,如果数据成员中没有指针的话即可不必写深拷贝了,毕竟重新申请内存空间会影响效率和性能。
例子
写一个学生类,其中包含属性为学生的名字指针,学生的id及学生的id指针,写出此类的构造,拷贝构造和析构函数的声明。
class Student
{
public:
//char name[128];
char* name;
long id;
long* pid;
public:
// 构造函数
Student(const char* name, long id);
// 拷贝构造
Student(const Student& other);
// 析构函数
~Student();
};
现在将类中的构造函数,拷贝构造函数及析构函数都实现一下。在实现拷贝构造函数时,我们可以去试一下浅拷贝和深拷贝的区别,比如学生的姓名指针,浅拷贝的写法就是使用this和other指针直接赋值,而深拷贝则是重新申请了一段内存空间,去存储复制过来的学生姓名。
Student::Student(const char* name, long id)
{
this->name = new char[128];
strcpy(this->name, name);
this->id = id;
this->pid = &this->id;
}
Student::Student(const Student& other)
{
//浅拷贝
this->name = other.name;
//深拷贝/位拷贝
this->name = new char[128];
strcpy(this->name, other.name);
this->id = other.id;
// 浅拷贝
this->pid = other.pid;
//深拷贝
this->pid = &this->id;
}
Student::~Student()
{
delete name;
name = nullptr;
}
完整代码如下:
#include <iostream>
using namespace std;
class Student
{
public:
//char name[128];
char* name;
long id;
long* pid;
public:
// 构造函数
Student(const char* name, long id);
// 拷贝构造
Student(const Student& other);
// 析构函数
~Student();
};
Student::Student(const char* name, long id)
{
this->name = new char[128];
strcpy(this->name, name);
this->id = id;
this->pid = &this->id;
}
Student::Student(const Student& other)
{
//浅拷贝
//this->name = other.name;
//深拷贝/位拷贝
this->name = new char[128];
strcpy(this->name, other.name);
this->id = other.id;
// 浅拷贝
//this->pid = other.pid;
//深拷贝
this->pid = &this->id;
}
Student::~Student()
{
delete name;
name = nullptr;
}
// 打印学生信息
void printStudentInfo(Student* ps)
{
cout << "姓名:" << ps->name <<
",学号:" << *ps->pid << endl;
}
int main()
{
Student s1("张三", 1001);
Student s2(s1);
s1.id = 10086;
printStudentInfo(&s1);
printStudentInfo(&s2);
return 0;
}
总结
大部分时候浅拷贝可以满足我们的需求,但是一旦类中的数据成员有指针或者拷贝的对象有对其他资源的引用时,我们最好重写拷贝构造函数为深拷贝。