浅拷贝问题的提出
首先,我们先看一段代码:
#include<string.h>
using namespace std;
class test
{
private:
char* p;
int len;
public:
test(const char* p)
{
len = strlen(p);
this->p = new char[len + 1];
strcpy(this->p,p);
}
~test()
{
delete p;
p = NULL;
len = 0;
}
};
int main()
{
test t1("hello");
test t2(t1);
return 0;
}
这段代码可以编译通过,但是当运行时会出现 ”core dumped“的错误:
1] 6928 abort (core dumped) ./a.out
那么这种错误是咋样引起的呢?这就是由于浅拷贝所引起的问题,下面,我们就来详细介绍一下浅拷贝以及解决方案。
原因分析
在分析原因之前,我们需要先了解一个问题,那就是拷贝构造函数的调用时机:
当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。以下情况都会调用拷贝构造函数:
(1)、一个对象以值传递的方式传入函数体
(2)、一个对象以值传递的方式从函数返回
(3)、一个对象需要通过另外一个对象进行初始化。
如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝,也称为浅拷贝。
默认对象的拷贝构造函数在完成对象成员的简单复制时,没有问题,但是当对象的数据资源是由指针指向的堆时,默认拷贝构造函数仅仅只复制指针,而没有重新开辟空间,这就导致有两个指针指向同一块堆上空间,当其中一个之后对象执行析构函数,释放内存 ,另一个对象的成员指针就变成了一个“野指针”,当其再次执行析构函数时,就会再次释放一次已经释放了的内存空间,就会出现“core dumped”错误!
注意:需要说明的是,在c++编译器提供的“=”操作也是浅拷贝
解决方法
采取深拷贝技术,即当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝。
在C++ 中具体的方法就是:
(1)、显式提供拷贝构造函数;
(2)、不使用编译器自带的等号运算符,重载“=”运算符。
#include<string.h>
using namespace std;
class test
{
private:
char* p;
int len;
public:
test(const char* p)
{
len = strlen(p);
this->p = new char[len + 1];
strcpy(this->p,p);
}
//拷贝构造函数
test(test & obj)
{
len = obj.len;
p = new char[len + 1];
strcpy(p,obj.p);
}
//重载等号运算符
void operator = (test &obj)
{
if(p != NULL )
{
delete p;
p = NULL;
len = 0;
}
p = new char(obj.len + 1);
strcpy(p,obj.p);
len = obj.len;
}
~test()
{
if(p != NULL)
{
delete[] p;
p = NULL;
len = 0;
}
}
};
int main()
{
test t1("hello");
//test t2(t1);
test t2("asd");
t2 = t1;
return 0;
}
采用深拷贝可以避免这个问题。所以说,在定义一个应该类时,显式提供拷贝构造函数,重载“=”运算符,避免一些不必要的问题。