【转自 https://www.cnblogs.com/always-chang/p/6107437.html】
在有指针的情况下,浅拷贝只是增加了一个指针指向已经存在的内存,而深拷贝就是增加一个指针并且申请一个新的内存,使这个增加的指针指向这个新的内存,采用深拷贝的情况下,释放内存的时候就不会出现在浅拷贝时重复释放同一内存的错误!
先考虑一种情况,对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数。
1 //main.cpp 2 3 #include <iostream> 4 #include "student.h" 5 int main() 6 { 7 Student s1; 8 Student s2(s1);//Student s2 = s1;//复制对象 9 10 return 0; 11 }
1 //student.h 2 3 #ifndef STUDENT_H 4 #define STUDENT_H 5 class Student 6 { 7 private: 8 int num; 9 char *name; 10 public: 11 Student(); 12 ~Student(); 13 }; 14 15 #endif
1 //student.cpp 2 3 #include "student.h" 4 #include <iostream> 5 using namespace std; 6 7 Student::Student() 8 { 9 name = new char(20); 10 cout << "Student" << endl; 11 12 } 13 Student::~Student() 14 { 15 cout << "~Student " << (int)name << endl; 16 delete name; 17 name = NULL; 18 }
执行结果:调用一次构造函数,调用两次析构函数,两个对象的指针成员所指内存相同,这会导致什么问题呢?
name指针被分配一次内存,但是程序结束时该内存却被释放了两次,会造成内存泄漏问题!
这是由于编译系统在我们没有自己定义拷贝构造函数时,会在拷贝对象时调用默认拷贝构造函数,进行的是浅拷贝!即对指针name拷贝后会出现两个指针指向同一个内存空间。
所以,在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
自己定义拷贝构造函数:
1 //student.h 2 3 #ifndef STUDENT_H 4 #define STUDENT_H 5 class Student 6 { 7 private: 8 int num; 9 char *name; 10 public: 11 Student();//构造函数 12 ~Student();//析构函数 13 Student(const Student &s);//拷贝构造函数,const防止对象被改变 14 }; 15 16 #endif
1 //student.cpp 2 3 #include "student.h" 4 #include <iostream> 5 #include <string.h> 6 using namespace std; 7 8 Student::Student() 9 { 10 name = new char(20); 11 cout << "Student " << endl; 12 } 13 14 Student::~Student() 15 { 16 cout << "~Student " << (int)name << endl; 17 delete name; 18 name = NULL; 19 } 20 21 Student::Student(const Student &s) 22 { 23 name = new char(20); 24 memcpy(name, s.name, strlen(s.name)); 25 cout << "copy Student " << endl; 26 }
执行结果:调用一次构造函数,一次自定义拷贝构造函数,两次析构函数。两个对象的指针成员所指内存不同。
总结:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
再说几句:
当对象中存在指针成员时,除了在复制对象时需要考虑自定义拷贝构造函数,还应该考虑以下两种情形:
1.当函数的参数为对象时,实参传递给形参的实际上是实参的一个拷贝对象,系统自动通过拷贝构造函数实现;
2.当函数的返回值为一个对象时,该对象实际上是函数内对象的一个拷贝,用于返回函数调用处。