C++深拷贝与浅拷贝实现原理
一、简介
-
在实际项目的开发过程中,哦们有时候需要用一个对象去初始化另一个对象,即原对象中的成员变量全部赋值给另一个成员变量。若采用系统默认的拷贝构造函数,就会存在浅拷贝的问题,会使得我们的程序出现一些问题。此时我们需要思考该怎么来解决这一问题呢?
-
在《高质量的C++编程》中,作者有提到这样一段话:
如果不主动编写拷贝构造函数和赋值函数,编译器将以"位拷贝"的方式自动生成缺省的函数,倘若勒种含有指针变量,那么两个缺省的函数就隐含了错误。以类string的两个对象为例,假设a.m_data的内容为"world",b中的内容为"hello"
现在将a赋值给b,缺省赋值函数的"位拷贝"意味着执行b.m_data原有的呢村没被释放,造成内存泄漏;二是b.m_data和a.m_data指向同一块内存,a和b任何一个变动都会影响另一方;三是对象呗析构的时候,m-data被释放了两次
-
上面一段话已经将浅拷贝的缺点说的很清楚了,下面做一下总结:
-
浅拷贝容易造成多个共用体公用一块内存,同一块内存资源释放多次,崩溃或者内存泄漏。
-
而深拷贝则会为每一个对象分配自己的内存空间,(特别是指在有指针成员的时候)但是深拷贝的拷贝构造函数是需要我们自己来编写的。
所以在使用到拷贝构造函数的时候,就有必要使用到自己定义的拷贝构造函数。
-
二、如何解决浅拷贝带来的问题
- 如上面已经提到的,解决浅拷贝的方法就是深拷贝了。
- 下面用代码说话,请见例子:
#include <iostream>
using namespace std;
class TEST;
{
private:
int *a;
int len;
public:
TEST(int *p,int n);
~TEST();
void show();
}
TEST::TEST(int *p,int n)
{
len = n;
a = new char[8]; //自定义的拷贝构造函数中,需要为指针变量重新复制。
for(int i = 0;i < n;i++)
{
a[i] = p[i]; //将A中值注意赋给P
}
}
TEST::~TEST()
{
delete []a; //释放分配的空间
}
void TEST::show()
{
cout << len << endl;
for(int i = 0;i < len;i++)
{
if(a[i] != 0)
cout << " " << a[i];
}
}
int main(void)
{
int b[] = {1,2,3,4,5,6,7};
TEST A(b,88);
A.show();
}
上面的拷贝构造函数不够正规,现在写一个常用的拷贝构造函数:
#include <iostream>
using namespace std;
class TEST;
{
private:
int *a;
int len;
public:
TEST(int *p,int n);
~TEST();
void show();
}
TEST::TEST(const TEST &T)
{
len = T.len;
a = new int[T.len];
for(int i = 0;i < len;i++)
{
if(a[i] != 0)
cout << a[i] << endl;
}
}
TEST::~TEST()
{
delete []a; //释放分配的空间
}
void TEST::show()
{
cout << len << endl;
for(int i = 0;i < len;i++)
{
if(a[i] != 0)
cout << " " << a[i];
}
}
int main(void)
{
int b[] = {1,2,3,4,5,6,7};
TEST A(b,88);
TEST B = A;
A.show();
}
三、总结
可以看出,当一个类中有指针成员时,拷贝构造函数中必须要为该指针成员重新申请内存空间,即是说在堆区为他申请一块内存。即需要使用深拷贝。因为,若采用浅拷贝的话,原对象的改制真变量的地址,就会指向同一块内存区域,这样的话,其中一个对象改变了该指针所指向的数据后,另一个对象也会受到影响。
结论:若采用拷贝构造函数时最好自己编写一深拷贝构造函数。