一、概念
深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。
浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。
浅拷贝示例:
#include<iostream>
using namespace std;
/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/
class Array
{
public:
Array();
Array(const Array &arr);
~Array();
void setCount(int count);
int getCount();
private:
int m_iCount;
};
Array::Array()
{
cout<<"Array()"<<endl;
}
Array::Array(const Array &arr)
{
m_iCount = arr.m_iCount;//浅拷贝
cout<<"Array()&"<<endl;
}
Array::~Array()
{
cout<<"~Array()"<<endl;
}
void Array::setCount(int count)
{
m_iCount = count;
}
int Array::getCount()
{
return m_iCount;
}
int main()
{
Array arr1;
arr1.setCount(5);
Array arr2(arr1);//用arr1实例化arr2 会调用arr1的拷贝构造函数
cout<<"arr2.m_iCount值"<<arr2.getCount()<<endl;//5 用arr1实例化arr2的时候,就将arr1中的m_iCount赋值给了arr2中的m_iCount
return 0;
}
运行结果:
用arr1实例化arr2的时候,就将arr1中的m_iCount赋值给了arr2中的m_iCount,这个代码中的拷贝构造函数中实现的是浅拷贝。那么浅拷贝会存在哪些问题呢?我们来看下一个代码
#include<iostream>
using namespace std;
/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/
class Array
{
public:
Array(int count);
Array(const Array &arr);
~Array();
void setCount(int count);
int getCount();
void printAddr();//地址查看函数
private:
int m_iCount;
int *m_pArr;
};
Array::Array(int count)
{
m_iCount = count;
m_pArr = new int[m_iCount];
cout<<"Array()"<<endl;
}
Array::Array(const Array &arr)
{
//浅拷贝是利用赋值的方式来拷贝的
m_pArr = arr.m_pArr;//浅拷贝 两个指向了同一段内存
m_iCount = arr.m_iCount;
cout<<"Array()&"<<endl;
}
Array::~Array()
{
delete []m_pArr;
m_pArr = NULL;
cout<<"~Array()"<<endl;
}
void Array::setCount(int count)
{
m_iCount = count;
}
int Array::getCount()
{
return m_iCount;
}
void Array::printAddr()
{
cout<<"m_pArr的值是"<<m_pArr<<endl;
}
int main()
{
Array arr1(5);
arr1.printAddr();
Array arr2(arr1);
arr2.printAddr();
return 0;
}
运行结果:
根据运行结果,我们发现通过arr1实例化出来的arr2的m_pArr的值与arr1相同,说明这两个指针指向了同一块内存,在调用析构函数的时候,arr1会释放一遍,arr2也会释放一遍,但是他们指向同一块内存,所以相当于让同一块内存释放两遍,会造成程序崩溃!!!
那么如何解决这样的问题呢?
利用深拷贝
#include<iostream>
using namespace std;
/*
深拷贝浅拷贝
1、定义一个Array类,数据成员m_iCount,成员函数:构造、析构、拷贝构造、成员封装,通过此例体会浅拷贝
2、增加数据成员m_pArr,并增加m_pArr地址查看函数,同时改造构造函数、拷贝构造和析构,体会深拷贝原理和必要性
*/
class Array
{
public:
Array(int count);
Array(const Array &arr);
~Array();
void setCount(int count);
int getCount();
void printAddr();//地址查看函数
void printArr();//打印数组中的元素
private:
int m_iCount;
int *m_pArr;
};
Array::Array(int count)
{
m_iCount = count;
m_pArr = new int[m_iCount];
for(int i = 0;i < m_iCount;i++)
{
m_pArr[i] = i;
}
cout<<"Array()"<<endl;
}
Array::Array(const Array &arr)
{
//深拷贝
m_iCount = arr.m_iCount;
m_pArr = new int[m_iCount];//先分配一段内存
for(int i = 0;i < m_iCount;i++)//将传入进来的对象对应的的数据拷贝到新申请的内存中
{
m_pArr[i] = arr.m_pArr[i];
}
cout<<"Array()&"<<endl;
}
Array::~Array()
{
delete []m_pArr;
m_pArr = NULL;
cout<<"~Array()"<<endl;
}
void Array::setCount(int count)
{
m_iCount = count;
}
int Array::getCount()
{
return m_iCount;
}
void Array::printAddr()
{
cout<<"m_pArr的值是"<<m_pArr<<endl;
}
void Array::printArr()
{
for(int i = 0;i < m_iCount;i++)
{
cout<<m_pArr[i]<<endl;
}
}
int main()
{
Array arr1(5);
arr1.printAddr();
arr1.printArr();
Array arr2(arr1);
arr2.printAddr();
arr2.printArr();
return 0;
}
运行结果:
我们可以看arr1中和arr2中的两个指针指向了不同的内存,arr2成功地拷贝了arr1中的数据,并且执行了两次析构函数
总结:浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针。
注意:
- 对一个已知对象进行拷贝,编译系统会自动调用一种构造函数——拷贝构造函数,如果用户未定义拷贝构造函数,则会调用默认拷贝构造函数,是浅拷贝。
- 在对含有指针成员的对象进行拷贝时,必须要自己定义拷贝构造函数,使拷贝后的对象指针成员有自己的内存空间,即进行深拷贝,这样就避免了内存泄漏发生。
学习参考:
一个完整的自定义类实现可参考:C++笔试题之String类的实现