深拷贝和浅拷贝
此概念针对的是对象之间的拷贝(拷贝构造,拷贝赋值)及对象内部的指针告别问题。
浅拷贝
行为: 当复制一个对象时,只复制对象本身的成员变量的值。如果成员变量是一个指针,那么只复制这个指针的地址值,而不是指针所指向的内存中的内容。
结果: 复制后,原对象和新对象的指针成员将指向同一块内存区域。
默认行为: C++ 编译器默认生成的拷贝构造函数和拷贝赋值运算符执行的是浅拷贝。
深拷贝
- 行为: 当复制一个对象时,不仅复制对象本身的成员变量的值,如果成员变量是一个指针,深拷贝会为新对象分配一块新的内存,并将原对象指针所指向的内存中的内容复制到这块新内存中。
- 结果: 复制后,原对象和新对象的指针成员将指向各自独立的内存区域,这两块内存区域的内容相同(在拷贝的时刻)。
- 实现: 需要程序员显式地定义拷贝构造函数和拷贝赋值运算符来实现。
浅拷贝问题
指针重复释放
描述
print(const Student stu)
发生了浅拷贝【使用编译器默认生成的拷贝构造】,导致stu1->data
和stu->data
指针指向相同,随着print函数结束,执行delete
销毁- 随着
main
结束,stu1
销毁,发现data
的指向已经销毁,出现double free
cpp
#include <iostream>
#include <cstring>
class Student
{
public:
// 默认构造
explicit Student(char *data, int length)
{
this->data = new char[length + 1];
this->length = length;
strcpy(this->data, data);
}
~Student()
{
delete[] data;
data = nullptr;
}
// 打印
void print(const Student stu)
{
std::cout << stu.data << std::endl;
}
public:
char *data;
int length;
};
int main()
{
char *data = "hello world";
Student stu1(data, 10);
stu1.print(stu1);
return 0;
}
解决
- 根据
Rule of Three
/Rule of Five
,我们自定义了任何一个(析构,拷贝构造,拷贝赋值,移动构造,移动赋值),都应该自定义其他几个- 根据实际情况,这里我们也应该编写拷贝构造函数,在拷贝构造内部使用new分配空间
cpp
#include <iostream>
#include <cstring>
class Student
{
public:
// 默认构造
explicit Student(char *data, int length)
{
this->data = new char[length + 1];
this->length = length;
strcpy(this->data, data);
}
// 拷贝构造
Student(const Student &stu)
{
this->data = new char[stu.length + 1];
this->length = stu.length;
strcpy(this->data, stu.data);
}
~Student()
{
delete[] data;
data = nullptr;
}
// 打印
void print(const Student stu)
{
std::cout << stu.data << std::endl;
}
public:
char *data;
int length;
};
int main()
{
char *data = "hello world";
Student stu1(data, 10);
stu1.print(stu1);
return 0;
}