关于浅拷贝会出现什么问题我们可以看这个例子:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char*pStr = "")//定义一个构造函数
{
if (pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
~String()
{
if (_pStr != NULL)
{
delete[]_pStr;
_pStr = NULL;
}
}
private:
char *_pStr;
};
void FunTest2()
{
String s1("Hello-");
String s2(s1);
}
int main()
{
FunTest2();
system("pause");
return 0;
}
然而程序运行时会崩溃,这是因为我们在String s2(s1);这一步的时候是进行值的拷贝,这样s1和s会指向同一块内存空间,于是在调用析构函数时我们先将s2析构掉时其实就是将s1指向的内存空间清理了,这时再来析构s1便会出现问题。同样将s2=s1也是同样的道理,程序同样会崩溃。
这时我们便需要用深拷贝来解决问题,再用深拷贝来解决String类的时候通常有两种方法,一种是普通的深拷贝,另一种是简洁版的。
普通深拷贝的步骤:
对于拷贝构造函数:开辟新空间,在进行内容的拷贝使其各自有各自的空间以至于在析构的时候各自有各自空间不至于程序运行崩溃
对于“=”运算符的重载:开辟新空间,释放原空间,拷贝内容。这样就不是进行简单的值拷贝,程序就不会因此崩溃。
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char*pStr = "")//定义一个构造函数
{
if (pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
String(const String &s)
{
_pStr = new char[strlen(s._pStr) + 1];
strcpy(_pStr, s._pStr);
}
String&operator=(const String&s)
{
if (this != &s)
{
char *pTemp = new char[strlen(s._pStr) + 1];
strcpy(pTemp, s._pStr);
delete[]_pStr;
_pStr = pTemp;
}
return *this;
}
~String()
{
if (_pStr != NULL)
{
delete[]_pStr;
_pStr = NULL;
}
}
private:
char *_pStr;
};
void FunTest2()
{
String s1("Hello-");
String s2 = s1;
}
int main()
{
FunTest2();
system("pause");
return 0;
}
简洁版的深拷贝其实就是交换,这样也不会使其同时指向同一块内存空间,析构的时候也不会出错
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char*pStr = "")//定义一个构造函数
{
if (pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
String(const String &s)
:_pStr(NULL)
{
String Str(s._pStr);
std::swap(_pStr, Str._pStr);
}
String &operator=(const String &s)
{
if (this != &s)
{
String Str(s._pStr);
std::swap(_pStr, Str._pStr);
}
return *this;
}
~String()
{
if (_pStr != NULL)
{
delete[]_pStr;
_pStr = NULL;
}
}
private:
char *_pStr;
};
void FunTest1()
{
String s1("Hello");
String s2(s1);
s2 = s1;
}
void FunTest2()
{
String s1("Hello-");
String s2(s1);
}
int main()
{
FunTest2();
system("pause");
return 0;
}
但是再在写简洁版的时候一定要注意拷贝构造函数中要将_pStr在拷贝构造函数的初始化列表中去置空。因为你在拷贝构造的时候_pStr没有明确的指向,因为S2还没有创建出来,他相当于是一个野指针,于是你去交换也没问题但是在交换完成之后你的Str.pStr就指向了一个随机的空间然后你要调用析构函数先将Str析构掉但此时他指向了不是用户自己创建的空间里,程序自然会崩溃。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
除了普通深拷贝和简洁版深拷贝来解决浅拷贝我们还可以用计数法来解决多次析构同一块内存空间的问题
也就是说如果多个对象指向同一块内存空间我们只析构一次
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char*pStr = "")//定义一个构造函数
{
if (pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1+4];
_pStr += 4;
GetRef(_pStr) = 1;
strcpy(_pStr, pStr);
}
}
String(const String &s)
{
cout << "调用拷贝构造函数" << endl;
_pStr = s._pStr;
++GetRef(_pStr);
}
~String()
{
cout << "~String" << endl;
Release();
}
int &GetRef(char *Str)
{
return *(int *)(Str-4);
}
void Release()
{
if (--(GetRef(_pStr)) == 0)
{
_pStr -= 4;
delete[]_pStr;
}
}
String &operator=(const String &s)
{
if (_pStr != s._pStr)
{
Release();
_pStr = s._pStr;
++GetRef(_pStr);
}
return (*this);
}
char &operator[](size_t index)//写时拷贝为了不使改变一个字符串而影响其他的
{
if (GetRef(_pStr) > 1)
{
char *Temp = new char[(strlen(_pStr)) + 1 + 4];
Temp += 4;
strcpy(Temp, _pStr);
--GetRef(_pStr);
_pStr = Temp;
GetRef(_pStr) = 1;
}
return _pStr[index];
}
const char& operator[](size_t index)const
{
return _pStr[index];
}
private:
char *_pStr;
};
void FunTest2()
{
String s1("Hello");
String s2(s1);
String s3;
s3 = s1;
s3[0] = '0';
}
int main()
{
FunTest2();
system("pause");
return 0;
}
2、写出普通版本深拷贝的String类,并实现以下函数(不能调用库函数)
// 不能使用库函数
size_t Size()const;
size_t Lengh()const;
char& operator[](size_t index);
const char& operator(size_t index);
bool operator>(const String& s);
bool operator<(const String& s);
bool operator==(const String& s);
bool operator!=(const String& s);
void Copy(const String& s)
bool strstr(const String& s);
String& operator+=(const String& s);
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<stdlib.h>
using namespace std;
class String
{
public:
String(const char*pStr = "")//定义一个构造函数
{
if (pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
String(const String &s)
{
_pStr = new char[strlen(s._pStr) + 1];
strcpy(_pStr, s._pStr);
}
String&operator=(const String&s)
{
if (this != &s)
{
char *pTemp = new char[strlen(s._pStr) + 1];
strcpy(pTemp, s._pStr);
delete[]_pStr;
_pStr = pTemp;
}
return *this;
}
~String()
{
if (_pStr != NULL)
{
delete[]_pStr;
_pStr = NULL;
}
}
size_t Size()const
{
int count = 0;
char *Temp = _pStr;
while (*Temp != '\0')
{
++count;
Temp++;
}
return count;
}
size_t Lengh()const
{
int count = 0;
char *Temp = _pStr;
while (*Temp != '\0')
{
++count;
Temp++;
}
return count;
}
char& operator[](size_t index)
{
if (index<0 || index>Size())
{
cout << "输入非法" << endl;
}
return _pStr[index];
}
const char& operator[](size_t index)const
{
if (index<0 || index>Size())
{
cout << "输入非法" << endl;
}
return _pStr[index];
}
bool operator>(const String& s)
{
char *Temp1 = (*this)._pStr;
char *Temp2 = s._pStr;
while (*Temp1 != '\0'&&*Temp2 != '\0')
{
if (*Temp1 == *Temp2)
{
Temp1++;
Temp2++;
}
if (*Temp1 > *Temp2)
{
return true;
}
if (*Temp1 < *Temp2)
{
return false;
}
}
if (*Temp1 != '\0'&&*Temp2 == '\0')
{
return true;
}
else
{
return false;
}
}
bool operator<(const String& s)
{
char *Temp1 = (*this)._pStr;
char *Temp2 = s._pStr;
while (*Temp1 != '\0'&&*Temp2 != '\0')
{
if (*Temp1 == *Temp2)
{
Temp1++;
Temp2++;
}
if (*Temp1 > *Temp2)
{
return false;
}
if (*Temp1 < *Temp2)
{
return true;
}
}
if (*Temp1 == '\0'&&*Temp2 != '\0')
{
return true;
}
else
{
return false;
}
}
bool operator==(const String& s)
{
char *Temp1 = (*this)._pStr;
char *Temp2 = s._pStr;
while (*Temp1 != '\0'&&*Temp2 != '\0')
{
if (*Temp1 == *Temp2)
{
Temp1++;
Temp2++;
}
if (*Temp1 > *Temp2)
{
return false;
}
if (*Temp1 < *Temp2)
{
return false;
}
}
if (*Temp1 == '\0'&&*Temp2 == '\0')
{
return true;
}
else
{
return false;
}
}
bool operator!=(const String& s)
{
return (!(*this == s));
}
void Copy(const String& s)
{
int i = 0;
char *pTemp1 = new char[strlen(s._pStr) + 1];
char *pTemp2 = s._pStr;
for (i = 0; pTemp2[i] != '\0'; i++)
{
pTemp1[i] = pTemp2[i];
}
pTemp1[i] = '\0';
delete[]_pStr;
_pStr = pTemp1;
}
bool strstr(const String& s)//判断s._pStr是否是(*this)._pStr的子串
{
char *Temp1 = (*this)._pStr;
char *Temp2 = s._pStr;
for (int i = 0; Temp1[i] != '\0'; i++)
{
int tmp = i;
int j = 0;
while (Temp1[i++] == Temp2[j++])
{
if (Temp2[j] == '\0')
{
return true;
}
}
i = tmp;
}
return false;
}
String& operator+=(const String& s)
{
char *Temp1 = (*this)._pStr;
char *Temp2 = s._pStr;
int len1 = strlen(Temp1);
int len2 = strlen(Temp2);
char *Buff = NULL;
char *end = NULL;
/*Buff = (char *)malloc(len1 + len2 + 1);*/
Buff = new char[len1 + len2 + 1];
strcpy(Buff, Temp1);
end = Buff + len1;
strcpy(end, Temp2);
delete[]_pStr;
_pStr = Buff;
return (*this);
}
private:
char *_pStr;
};
void FunTest2()
{
String s1("kkllll");
String s2 ("ell");
s1.Copy(s2);
}
int main()
{
FunTest2();
system("pause");
return 0;
}