浅拷贝与深拷贝

浅拷贝与深拷贝的区别:浅拷贝只能拷贝数据中的栈内存空间数据,深拷贝是拷贝所以数据,包括堆内存中的数据

浅拷贝

在之前讲过的拷贝构造函数,其实就是一种 浅拷贝
但是在浅拷贝中,无法拷贝堆内存中的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Student1
{
public:

int age;
char * name;

Student1() { cout << "无参构造函数" << endl; }

Student1(char * name) :Student1(name, 99) { cout << "一个入参 构造函数" << endl; }

Student1(char * name, int age) {
cout << "两个入参构造函数" << endl;

this->name = (char *) malloc(sizeof(char * ) * 10);
strcpy(this->name, name);

this->age = age;
}

~Student1() {
cout << "析构函数" << endl;

free(this->name);
this->name = NULL;
}

};

如上所示,Student 类中,在构造函数中,name 是动态开辟在堆内存上的,在析构函数中,会使用free释放 name 在堆内存的内存空间

1
2
3
4
5
6
7
8
9
void main() {

Student1 s1;
Student1 s2 = s1;

cout << &s1 << endl;
cout << &s2 << endl;

}

当我们执行完上述代码时,会运行异常,
Student1 s2 = s1; 会执行 拷贝构造函数,将 s2 的值都复制给 s1
但是之前说过 拷贝构造函数是 浅拷贝
浅拷贝 是不会拷贝堆内存中的数据的, 所以其实 s2 与 s1 中的 name 指向的同一块堆内存空间,
当main函数执行完毕,出栈时,会调用 s2、s1的析构函数,当指向s1的析构函数时,会释放 name 中的堆内存空间。
等到执行 s2 的析构函数时,又会执行 free(this->name); 但此时 this->name已经是一块指向NULL的指针了,此时就会抛出异常。

深拷贝

上面讲了深拷贝与浅拷贝的区别,以及默认的拷贝构造函数是一次浅拷贝,接下来手动实现一下 深拷贝

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class Student
{
public:

int age;
char * name;

Student() { cout << "无参构造函数" << endl; }

Student(char * name) :Student(name, 99) {
cout << "一个参数构造函数 this:" << (int)this << endl;
}

Student(char * name, int age) {
this->name = (char *)malloc(sizeof(char *)* 10);
strcpy(this->name, name);
this->age = age;
}

~Student() {
cout << "析构函数 &this->name:" << (int)this->name << endl;

free(this->name);
this->name = NULL;
}

Student(const Student & stu) {


cout << "stu的地址 &stu:" << (int)&stu << " this:" << (int)this << endl;

// 重新开辟一个堆内存存放新的 stu对象中的 name
this->name = (char *)malloc(sizeof(char *)* 10);
// 复制name中的值
strcpy(this->name, name);
this->age = stu.age;

}


};

void showStudent(Student stu) {
cout << "showStudent的内存地址" << (int)&stu << " " << stu.name << "," << stu.age<< endl;
}

void main() {
Student stu("justin", 31);

showStudent(stu);
showStudent(stu);
showStudent(stu);
showStudent(stu);
showStudent(stu);

getchar();
}

如上所述,在函数 showStudent 中会调用拷贝构造函数 然后执行完毕出栈,调用 析构函数,
但上述代码不会出现异常,因为在我们自定义的拷贝构造函数中,我们手动实现了堆内存的拷贝。并每次执行析构函数时,又会释放掉name的堆内存。