C++浅拷贝与深拷贝探究
浅拷贝与深拷贝的概念是在类的复制/拷贝构造函数中出现的。
拷贝构造函数使用场景
- 对象作为参数,以值传递方式传入函数(要调用拷贝构造函数将实参拷贝给函数栈中的形参)
- 对象作为返回值,以值方式返回(要调用拷贝构造函数,将要传出的对象拷贝给一个外作用域的临时对象)
- 用一个对象去初始化另外一个对象
浅拷贝:直接使用 “=” 赋值,拷贝给定对象属性。(如果是一般类型,那么就相当于赋值运算符。如果有指针,赋值后就相当于拷贝双方指向同一个地址)
private:
xxx *pStr;
......
pStr = m.pStr;//拷贝的是地址
深拷贝:在赋值之前,先给指针申请一段内存,而后拷贝给定对象属性值。(即:先申请内存,再赋值)
pStr = new xxx[xxx];//先申请内存
对pStr赋值//再拷贝值
实例说明
浅拷贝
1 #pragma once 2 class Demo 3 { 4 private: 5 int m_icount; 6 int *pStr; 7 public: 8 Demo(int icount);//构造函数 9 Demo(const Demo &d);//拷贝构造函数(浅拷贝) 10 ~Demo();//析构函数 11 void setValue(int icount); 12 void printValue(); 13 void printAddr(); 14 };
1 #include "Demo.h" 2 #include <iostream> 3 using namespace std; 4 5 Demo::Demo(int icount) 6 { 7 m_icount = icount; 8 pStr = new int[m_icount]; 9 for (int i = 0; i < m_icount; i++) 10 { 11 pStr[i] = i; 12 } 13 cout << "\nDemo(int value)构造函数被调用!" << endl; 14 } 15 16 Demo::Demo(const Demo &d) 17 { 18 m_icount = d.m_icount; 19 //浅拷贝 20 pStr = d.pStr;//浅拷贝,赋地址 21 cout << "Demo(const Demo &d)浅拷贝构造函数被调用!" << endl; 22 23 //深拷贝 24 /*pStr = new int[m_icount]; 25 for (int i = 0; i < m_icount; i++) 26 { 27 pStr[i] = d.pStr[i]; 28 } 29 cout << "\nDemo(const Demo &d)深拷贝构造函数被调用!" << endl;*/ 30 } 31 32 Demo::~Demo() 33 { 34 delete[]pStr; 35 pStr = NULL; 36 cout << "\n~Demo()析构函数被调用!" << endl; 37 } 38 39 void Demo::setValue(int icount) 40 { 41 m_icount = icount; 42 cout << "\nsetValue(int icount)函数被调用!" << endl; 43 } 44 45 void Demo::printValue() 46 { 47 for (int i = 0; i < m_icount; i++) 48 { 49 cout << pStr[i] << " "; 50 } 51 cout << "\nDemo::printValue()函数被调用!" << endl; 52 } 53 54 void Demo::printAddr() 55 { 56 cout << pStr << endl; 57 } 58 59 int main() 60 { 61 Demo d1(6); 62 cout << "\nd1 : "; 63 d1.printValue(); 64 65 d1.setValue(5); 66 cout << "\nd1 : "; 67 d1.printValue(); 68 69 Demo d2(d1); 70 cout << "\nd2 :"; 71 d2.printValue(); 72 73 cout << "\nd1 addr : "; 74 d1.printAddr(); 75 cout << "\nd2 addr : "; 76 d2.printAddr(); 77 78 system("pause"); 79 }
//代码中的拷贝构造函数形参 const Demo &d
//const: 禁止在传参过程中改变参数对象
//&d:引用传递,参数如果不是引用传递,那么在将实参拷贝给形参时就要调用拷贝构造函数,而函数自身就是拷贝构造函数,就会不断调用自己,陷入递归,无法自拔。->拷贝构造函数必须使用引用传递!!!
结果
那么,对于两者中任意一个对象进行操作就势必会改变另外一个,这是我们一般不希望看到的。这时候,我们就需要深拷贝。为即将产生的新对象重新申请存储空间。
深拷贝
1 #include "Demo.h" 2 #include <iostream> 3 using namespace std; 4 5 Demo::Demo(int icount) 6 { 7 m_icount = icount; 8 pStr = new int[m_icount]; 9 for (int i = 0; i < m_icount; i++) 10 { 11 pStr[i] = i; 12 } 13 cout << "\nDemo(int value)构造函数被调用!" << endl; 14 } 15 16 Demo::Demo(const Demo &d) 17 { 18 m_icount = d.m_icount; 19 //浅拷贝 20 /*pStr = d.pStr;//浅拷贝,赋地址 21 cout << "Demo(const Demo &d)浅拷贝构造函数被调用!" << endl;*/ 22 23 //深拷贝 24 pStr = new int[m_icount]; 25 for (int i = 0; i < m_icount; i++) 26 { 27 pStr[i] = d.pStr[i]; 28 } 29 cout << "\nDemo(const Demo &d)深拷贝构造函数被调用!" << endl; 30 } 31 32 Demo::~Demo() 33 { 34 delete[]pStr; 35 pStr = NULL; 36 cout << "\n~Demo()析构函数被调用!" << endl; 37 } 38 39 void Demo::setValue(int icount) 40 { 41 m_icount = icount; 42 cout << "\nsetValue(int icount)函数被调用!" << endl; 43 } 44 45 void Demo::printValue() 46 { 47 for (int i = 0; i < m_icount; i++) 48 { 49 cout << pStr[i] << " "; 50 } 51 cout << "\nDemo::printValue()函数被调用!" << endl; 52 } 53 54 void Demo::printAddr() 55 { 56 cout << pStr << endl; 57 } 58 59 int main() 60 { 61 Demo d1(6); 62 cout << "\nd1 : "; 63 d1.printValue(); 64 65 d1.setValue(5); 66 cout << "\nd1 : "; 67 d1.printValue(); 68 69 Demo d2(d1); 70 cout << "\nd2 :"; 71 d2.printValue(); 72 73 cout << "\nd1 addr : "; 74 d1.printAddr(); 75 cout << "\nd2 addr : "; 76 d2.printAddr(); 77 78 system("pause"); 79 }
结果
简言之,浅拷贝就是把别人的地址拿过来作为自己的,双方共同维护一个对象,“绑在一块,一损俱损”;而深拷贝则是赋值之前为新对象申请了额外的空间,所以独立于被拷贝对象,“咱没关系,爱咋咋地”!
拷贝构造函数使用原则
- 含有指针类型的成员或者含有动态内存空间的对象,必须提供自定义的拷贝构造函数。
- 在提供拷贝构造函数的同时,也要考虑实现自定义的赋值运算符。
怎样区别
赋值运算还是拷贝函数?有无新对象产生
深拷贝还是浅拷贝?有无指针成员和动态内存空间