对于String类有多种构造形式,下面就来看看String类几种不同构造形式之间的不同吧。
1.浅拷贝
浅拷贝是最简单的
class String
{
public:
String()
:_pStr(new char[1])
{
*_pStr = '\0';
}
String(const char* pStr)
:_pStr(new char[strlen(pStr) + 1])
{
strcpy(_pStr,pStr);
}
~String()
{
if(_pStr)
{
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
它的代码量是最少的,但是却有问题。
它的拷贝构造函数与赋值运算符重载都没有重新分配空间,只是把原来的空间重新命名了两次,所以在析构的时候就出错了。析构第二个对象时,空间已经不存在了,所以就会报错。
在继续执行就会报错。
2.深拷贝
深拷贝是在浅拷贝的基础上重写了拷贝构造函数和赋值运算符重载函数,给对象重新开辟了空间,并将原来对象的内容拷贝过去。
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)
{
delete[] _pStr;
_pStr = new char[strlen(s._pStr) + 1];
strcpy(_pStr, s._pStr);
}
return *this;
}
~String()
{
if(_pStr)
{
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
size_t Size()const
{
return strlen(_pStr);
}
size_t Length()const
{
return strlen(_pStr);
}
char& operator[](size_t index)
{
return _pStr[index];
}
const char& operator[](size_t index)const
{
return _pStr[index];
}
//const char& operator(size_t index);
bool operator>(const String& s)
{
int len = strlen(_pStr);
for(int i = 0; i <= len; i++)
{
if(_pStr[i] > s._pStr[i])
{
return true;
}
}
return false;
}
bool operator<(const String& s)
{
return !((*this > s) ||(*this == s));
}
bool operator==(const String& s)
{
int len = strlen(_pStr);
for(int i = 0; i <= len; i++)
{
if(_pStr != s._pStr)
return false;
}
return true;
}
bool operator!=(const String& s)
{
return !(*this == s);
}
void Copy(const String& s)
{
if(this != &s)
{
delete[] _pStr;
_pStr = new char[strlen(s._pStr) + 1];
strcpy(_pStr, s._pStr);
}
}
bool strstr(const String& s)
{
int i = 0;
while(_pStr != NULL)
{
int j = 0;
while(s._pStr != NULL)
{
if(_pStr[i] != s._pStr[j])
{
break;
}
}
if(j == strlen(_pStr))
return true;
}
return false;
}
String& operator+=(const String& s)
{
_pStr = (char*)realloc(_pStr, (strlen(_pStr) + strlen(s._pStr) + 1));
strcat(_pStr, s._pStr);
return *this;
}
3.简洁版的深拷贝
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)
{
String strTemp(s._pStr);
_pStr = new char[strlen(s._pStr)+1];
std::swap(_pStr,strTemp._pStr);
}
String & operator=(const String& s)
{
if(this != &s)
{
String strTemp(s);
std::swap(_pStr, strTemp._pStr);
}
return *this;
}
~String()
{
if(_pStr)
{
delete[] _pStr;
_pStr = NULL;
}
}
private:
char* _pStr;
};
构造函数与析构函数没有变化,使用swap()重写了拷贝构造函数与赋值运算符重载函数。
但是要注意,pStr = new char[strlen(s._pStr)+1];不能缺少,因为交换后的strTemp._pStr是一个野指针,没有合法的地址空间,析构时就会出错,所以需要先给_pStr一段空间,或者是在交换后将_pStr置为空指针->_pStr=NULL;
String(const String& s)
{
String strTemp(s._pStr);
_pStr = new char[strlen(s._pStr)+1];
std::swap(_pStr,strTemp._pStr);
}
4.引用计数的String类
class String
{
public:
String(const char* pStr = "")
:_pCount(new int(1))
{
if(pStr == NULL)
{
_pStr = new char[1];
*_pStr = '\0';
}
else
{
_pStr = new char[strlen(pStr) + 1];
strcpy(_pStr, pStr);
}
}
String(const String& s)
:_pStr(s._pStr)
,_pCount(s._pCount)
{
++(*_pCount);
}
String & operator=(const String& s)
{
if(_pStr != s._pStr)
{
if(--(*_pCount) == 0)
{
delete[] _pStr;
delete[] _pCount;
}
_pStr = s._pStr;
_pCount = s._pCount;
++(*_pCount);
}
return *this;
}
~String()
{
if(--(*_pCount) == 0 && _pCount)
{
delete[] _pStr;
_pStr = NULL;
delete[] _pCount;
_pCount = NULL;
}
}
private:
char* _pStr;
int* _pCount;
};
class String
{
public:
String(char* pStr = "")
{
_pStr = new char[strlen(pStr) + 1+4];//前4个字节存放count值
_pStr+=4;
strcpy(_pStr, pStr);
GetCount() = 1;
}
String(const String& s)
:_pStr(s._pStr)
{
++GetCount();
}
String & operator=(const String& s)
{
if(this != &s)
{
Relese();//释放原空间
_pStr = s._pStr;
++GetCount();
}
return *this;
}
char& operator[](size_t index)
{//避免修改其他对象的值
if(GetCount()>1)
{
--GetCount();
char* temp=new char[strlen(_pStr)+1+4];
temp+=4;
strcpy(temp, _pStr);
_pStr = temp;
GetCount() = 1;
}
return _pStr[index];
}
int& GetCount()
{
return *((int*)_pStr - 1);
}
void Relese()
{
if(--GetCount() == 0)//如果只有1个对象在使用
{
_pStr -= 4;
delete[] _pStr;
_pStr = NULL;
}
}
~String()
{
Relese();
}
private:
char* _pStr;
};
写时拷贝是在申请空间时多申请4个用来存放指向这段空间的指针的个数。他是在引用计数得基础上修改的,用一个_pCount指针来存放指向该段空间的个数,只有在需要修改某个值时,为了避免修改其他对象的值,需要重新开辟空间。否则,这样可以节省内存,要析构某个函数时只需要将(_pCount)–就行了。