浅拷贝,实际上就是对指针进行拷贝,两个指针指向同一块地址空间。而深拷贝不仅仅是对指针进行拷贝,而且对指针指向的内容进行拷贝。经深拷贝两个指针指向不同的地址空间。
class String
{
public:
String(const char* _pStr = "")//构造函数
:m_iSize(strlen(_pStr)+1)
{
m_pStr = new char[m_iSize];
strcpy(m_pStr, _pStr);
m_pStr[m_iSize] = '\0';
}
String(const String& _string)//拷贝构造函数
:m_iSize(_string.m_iSize)
{
if (this != &_string)
{
m_pStr = _string.m_pStr;
}
}
~String()//析构函数
{
if (NULL != m_pStr)
{
delete[] m_pStr;
m_pStr = NULL;
}
}
private:
char* m_pStr;
int m_iSize;
};
测试函数
void FunTest()
{
String str1("hello");
String str2(str1);
}
这个程序会出现问题,导致程序奔溃。
代码分析:
1.String str1("hello");
调用的是String类的构造函数。首先,给m_pStr分配空间,然后,将形参的内容拷贝到m_pStr中。
2.String str2(str1);
调用的是String类的拷贝构造函数。首先,根据形参m_iSize初始化m_iSize,然后,让m_pStr指向形参m_pStr空间。
3.函数执行过程中,局部变量str2被析构,调用析构函数,将str2.m_pStr所在的空间释放掉。
4.str1被析构,调用析构函数,str1.m_pStr一直指向申请空间,去释放已经str2.m_pStr释放的空间,相同的空间被释放了两次,因此程序奔溃。
浅拷贝:也称位拷贝,编译器只是直接将指针的值拷贝过来,结果多个对象共用同一块空间,当一个对象将这块空间释放之后,另一些对象不知道这块空间已经还给了系统,以为有效,所以在对这块空间进行操作时,发生了访问违规。那么,深拷贝就能解决浅拷贝不能解决的问题,构造对象时拷贝一块跟当前对象一样的大数据块,析构时各自释放各自的数据块。
深拷贝采取了在堆内存中申请新的空间来存储数据,在拷贝构造函数中申请了新的内存空间,使得两个对象的成员变量不指向同一个内存空间,除非需要这样做。
class String
{
public:
String(const char *pStr = "")//构造函数
{
if (NULL == pStr)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
~String()//析构函数
{
if (NULL != _pStr)
{
delete[] _pStr;
_pStr = NULL;
}
}
/*
//深拷贝
//普通版
String(const String &s) //拷贝构造
:_pStr(new char[strlen(s._pStr) + 1])
{
strcpy(_pStr, s._pStr);
}
String& operator=(const String& s)//赋值运算符重载
{
if (this != &s)
{
char* tmp = new char[strlen(s._pStr) + 1];//开辟新空间
strcpy(tmp, s._pStr);//拷贝
delete[] _pStr;//释放旧空间
strcpy(_pStr, tmp);
}
return *this;
}
*/
//简洁版
String(const String &s)//拷贝构造
:_pStr(NULL)//解决_pStr为野指针的情况
{
String tmp(_pStr);
swap(_pStr, tmp._pStr);//交换
}
String& operator=(const String& s)//赋值运算符重载
{
if (this != &s)
{
String tmp(_pStr);
swap(_pStr, tmp._pStr);//交换
}
return *this;
}
private:
char* _pStr;
};
void FunTest()
{
String str1("hello");
String str2(str1);
}