25. 深浅拷贝
一、深浅拷贝问题
在 C++ 中,对象的拷贝可以分为浅拷贝(Shallow Copy)和深拷贝(Deep Copy)。
浅拷贝 是指当一个对象拷贝另一个对象时,只是简单地复制了对象的各个成员变量的值,包括指向其他对象的指针。这意味着,如果源对象和目标对象中的成员变量是指针类型,那么浅拷贝只会复制指针的值,而不是指针指向的数据。因此,源对象和目标对象将共享相同的指针指向的数据,这可能会导致潜在的问题,比如当一个对象被销毁时,它可能会释放资源,导致另一个对象中的指针悬空,又比如两个对象将共享同一块内存,如果一个对象改变了那块内存的内容,另一个对象也会受到影响。默认情况下,编译器会为类生成浅拷贝构造函数和浅拷贝赋值运算符。
深拷贝 是指当一个对象拷贝另一个对象时,不仅复制了对象的各个成员变量的值,而且还复制了这些成员变量所指向的数据。这意味着,如果源对象和目标对象中的成员变量是指针类型,那么深拷贝会分配新的内存,为指针指向的数据创建一个新的副本,并将指针指向这个新副本。因此,源对象和目标对象将拥有独立的资源,新旧对象之间不会共享任何非静态成员,互不影响。
在 C++ 中,默认的拷贝构造函数和拷贝赋值运算符通常执行浅拷贝。如果你需要实现深拷贝,你需要在你的类中自定义拷贝构造函数和拷贝赋值运算符,以确保正确地复制所有成员变量所指向的数据。
二、什么是浅拷贝
浅拷贝 只是简单地复制对象的所有成员变量,包括指针。如果对象的成员变量中有动态分配的内存(如指针指向的内存),则浅拷贝只是复制了指针本身,而不是它所指向的内存。这意味着两个对象现在指向同一块内存区域。
class School
{
private:
string name;
string address;
public:
School(void);
School(string name, string address);
void showInfo(void);
string getName(void);
void setName(string name);
string getAddress(void);
void setAddress(string address);
};
School::School(void) : name(""), address("") {}
School::School(string name, string address) : name(name), address(address) {}
void School::showInfo(void)
{
cout << "School{name: " << name << ", address: " << address << "}" << endl;
}
string School::getName(void)
{
return name;
}
void School::setName(string name)
{
this->name = name;
}
string School::getAddress(void)
{
return address;
}
void School::setAddress(string address)
{
this->address = address;
}
class Student
{
private:
string name;
int age;
School *school;
public:
Student(void);
Student(string name, int age);
void showInfo(void);
string getName(void);
void setName(string name);
int getAge(void);
void setAge(int age);
School * getSchool(void);
void setSchool(School *school);
};
Student::Student(void) : name(""), age(0), school(new School()) {}
Student::Student(string name, int age) : name(name), age(age) {}
void Student::showInfo(void)
{
cout << "Studet{name: " << name << ", age: " << age << ", school: " << school->getName() << "}" << endl;
}
string Student::getName(void)
{
return name;
}
void Student::setName(string name)
{
this->name = name;
}
int Student::getAge(void)
{
return age;
}
void Student::setAge(int age)
{
this->age = age;
}
School * Student::getSchool(void)
{
return school;
}
void Student::setSchool(School *school)
{
this->school = school;
}
#include <iostream>
using namespace std;
int main(void)
{
School *school = new School("友枝小学", "京都府京都中京区");
Student s1("木之本樱", 10);
s1.setSchool(school);
s1.showInfo();
Student s2(s1);
s2.setAge(12);
s2.getSchool()->setName("友枝中学");
s2.showInfo();
s1.showInfo();
delete school;
return 0;
}
三、什么是深拷贝
深拷贝不仅会复制对象的所有成员变量,还会为指针所指向的内存分配新的内存,并将内容复制到新的内存区域。这样,两个对象就有各自独立的内存区域。如果我们想要实现深拷贝,需要自己重写类的拷贝构造函数。
在 Student 类中重写拷贝构造函数和析构函数。
class Student
{
private:
string name;
int age;
School *school;
public:
Student(void);
Student(string name, int age);
Student(const Student &student);
~Student(void);
void showInfo(void);
string getName(void);
void setName(string name);
int getAge(void);
void setAge(int age);
School * getSchool(void);
void setSchool(School *school);
};
Student::Student(void) : name(""), age(0), school(new School()) {}
Student::Student(string name, int age) : name(name), age(age) , school(new School()) {}
Student::Student(const Student &student) : name(student.name), age(student.age)
{
school = new School(student.school->getName(), student.school->getAddress());
}
Student::~Student(void)
{
if (school != nullptr)
{
delete school;
}
}
void Student::showInfo(void)
{
cout << "Studet{name: " << name << ", age: " << age << ", school: " << school->getName() << "}" << endl;
}
string Student::getName(void)
{
return name;
}
void Student::setName(string name)
{
this->name = name;
}
int Student::getAge(void)
{
return age;
}
void Student::setAge(int age)
{
this->age = age;
}
School * Student::getSchool(void)
{
return school;
}
void Student::setSchool(School *school)
{
this->school = school;
}
修改 main() 函数:
int main(void)
{
School *school = new School("友枝小学", "京都府京都中京区");
Student s1("木之本樱", 10);
s1.setSchool(school);
s1.showInfo();
Student s2(s1);
s2.setAge(12);
s2.getSchool()->setName("友枝中学");
s2.showInfo();
s1.showInfo();
return 0;
}