- string类模拟实现
模拟实现string类
1、首先我们需要成员变量_size记录字符串的有效字符长度,_capacity记录容量
2、String类的6个默认成员函数
(1)构造函数
String(const char* str = "")
{
if (nullptr == str)
str = "";
_capacity = strlen(str);
_size = _capacity;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
String(size_t n, char ch)
:_str(new char[n + 1])
, _capacity(n)
, _size(n)
{
memset(_str, ch, _size);
_str[_size] = '\0';
}
String(const char* pstr, size_t n)
{
size_t length = strlen(pstr);
_size = length > n ? n : length;
_capacity = _size;
_str = new char[_capacity + 1];
for (size_t i = 0; i < _size; ++i)
{
_str[i] = pstr[i];
}
_str[_size] = '\0';
}
构造函数是创建对象,申请内存,设置_size和_capacity变量,上面实现了3个构造函数,第一个就只是利用字符串创建,第二个是创建n个字符为ch的字符串,第三个是
将字符串的n个创建。
(2)析构函数
就是出了作用域后,进行对象资源的清理
(3)拷贝构造函数
String(const String& s)//深拷贝
:_str(new char[strlen(s._str) + 1])
{
strcpy(_str, s._str);
_size = s._size;
_capacity = s._capacity;
}
这里的拷贝构造函数是深拷贝,给_str重新开辟重新开辟一块空间,使用strcpy函数进行拷贝。
(4)赋值运算符重载
String& operator=(const String& s)//赋值运算符重载
{
if (this != &s)
{
char* ptr = new char[strlen(s._str) + 1];
strcpy(ptr, s._str);
delete[]_str;
_str = ptr;//这种方式比较好,因为上一种方法实现释放空间,后来再申请空间,可是万一后面申请空间失败,就会出错
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
这种写法是比较好的,先申请一块临时空间,利用strcpy函数进行拷贝,释放原来的空间,让它指向临时空间,改变其他变量.
3、增删改查
首先先来实现reserve函数,它是用来扩容的
void String::reserve(size_t newcapacity)
{
if (newcapacity > capacity())
{
char* pStr = new char[newcapacity + 1];
strcpy(pStr, _str);
delete[]_str;
_str = pStr;
_capacity = newcapacity;
}
}
reserve实现原理是改变容量大小,不改变有效元素个数,如果要申请的容量大于旧容量,进行扩容,反之就不扩容,如果是缩小的话,在vs下,如果>=15,不会改变容量,否则缩小容量;上面的实现原理就是先申请一个新空间,利用strcpy进行拷贝,释放原来的空间,让它指向新的空间,改变容量。
(1)增
尾插一个字符:
void String::Pushback(char ch)//尾插一个字符
{
if (_size == _capacity)
{
reserve(_capacity * 2);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
首先判断是否需要增容,然后尾插即可。
尾插一个字符串
void String::PushBack(const char* str)
{
size_t str_size = strlen(str);
if (_size + str_size == _capacity)
{
_capacity += str_size;
reserve(_capacity);
}
while (*str != '\0')
{
_str[_size++] = *str++;
}
_str[_size] = '\0';
}
先判断是否需要增容,然后将字符串的字符一个一个尾插。
头插一个字符:
void String::Pushfront(char ch)
{
if (_size == _capacity)
{
reserve(_capacity * 2);
}
for (int i = _size; i >= 0; --i)
{
_str[i] = _str[i - 1];
}
_str[0] = ch;
_size += 1;
_str[_size] = '\0';
}
判断是否增容,然后从后往前将所有的字符向后移一位,然后将字符插入头部,改变有效元素个数。
头插一个字符串:
void String::Pushfront(const char* str)
{
size_t strsize = strlen(str);//要插入的字符串的长度
if (strsize + _size == _capacity)
{
_capacity = strsize + _size + 1;
reserve(_capacity);
}
size_t indexPrev = _size;
size_t indexLast = _size + strsize - 1;
while (indexPrev)
{
_str[indexLast--] = _str[--indexPrev];//将当前字符串向后移strsize距离给前面腾出位置
}
while (*str != '\0')
{
_str[indexPrev++] = *str++;
}
_size += strsize;
_str[_size] = '\0';
}
首先计算出要插入的字符的长度,然后判断容量是否足够,不够就增容,定义一个变量indexPre表示原字符串有效元素个数,定义一个变量indexLast表示插入后的有效元素个数,将当前字符串移出要插入的字符串的长度,然后插入。
在指定位置插入字符:
void String::insert(size_t pos, char ch)
{
assert(pos <= _size);
reserve(_size + 1);
size_t end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
}
首先要保证插入的位置合法,然后增容,将pos后面的元素向后移一位,将指定字符插入到pos位置,改变有效元素个数。
在指定位置插入字符串:
void String::insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
size_t end = strlen(_str);
int i = 0;
reserve(_size + len);
while (end >= pos)
{
_str[end + len] = _str[end];//pos后面的统一向后移
--end;
}
while (*str)
{
_str[pos++] = *str++;
}
_size += len;
}
先判断插入位置是否合法,计算出要插入的字符串长度,扩容,将pos后的元素向后移要插入字符串的长度,插入到pos后,改变有效元素个数。
(2)删
尾删一个字符:
void String::Popback()
{
if (_size == 0)
{
return;
}
_size--;
_str[_size] = '\0';
}
只需要改变有效字符个数即可
头删一个字符:
void String::Popfront()
{
for (size_t i = 0; i < _size - 1; ++i)
{
_str[i] = _str[i + 1];
}
_size--;
_str[_size]='\0';
}
将第一个元素后的元素向前移一个位置,改变有效元素个数。
在指定位置之后删除长度为n的字符:
void String::erase(size_t pos, size_t n)//从pos开始删除长度为n的字符串
{
assert(pos <= _size-1);
while (_str[pos+n-1])
{
_str[pos-1] = _str[pos+n-1];
pos++;
}
_str[pos - 1] = '\0';
_size -= n;
}
保证pos合法,然后将n个元素之后的剩下的元素移到前面,再给末尾加上’\0’。
(3)改
[]运算符的重载:
char& String:: operator[](size_t index)
{
assert(index < size());
return _str[index];
}
const char& String:: operator[](size_t index)const
{
assert(index < size());
return _str[index];
}
返回字符串数组的字符的引用。
(4)查
查找一个字符:
size_t String::find(char ch)
{
for (size_t i = 0; i < _size; ++i)
{
if (ch == _str[i])
return i;
}
return -1;
}
查找一个字符串:
size_t String::find(const char* str)
{
size_t index_str = 0;
while (_str[index_str] != '\0')
{
if (_str[index_str] == *str)
{//继续往后查找
size_t find_index = index_str;
size_t str_index = 0;
while (1)
{
if (str[str_index] == '\0')//遍历完了str
{
return index_str;
}
if (_str[find_index] != str[str_index])
break;
find_index++;
str_index++;
}
}
index_str++;
}
return -1;
}
4、运算符重载
(1)+运算符重载
加一个字符(不要更改原来的对象)
String String::operator+(char ch)
{
String strTemp(_str);
strTemp.insert(_size, ch);
return strTemp;
}
加一个字符串(不要更改原来的对象)
String String::operator+(const char* str)
{
String strTemp(_str);
strTemp.insert(_size, str);
return strTemp;
}
(2)+=运算符重载
+=一个字符
String& String:: operator+=(char ch)
{
insert(_size, ch);
return *this;
}
+=一个字符串
String& String:: operator+=(const String& s)
{
size_t leftByte = _capacity - _size;//剩了多少空间
size_t totalByte = s.size();//拼接的字符串需要的空间
if (totalByte>leftByte)
{
reserve(_size + totalByte);//申请容量
}
strcpy(_str + _size, s._str);//拼接到_str后
_size += totalByte;
return *this;
}
(3)>运算符重载
bool String::operator>(const String& s)
{
char* pstr1 = _str;
char* pstr2 = s._str;
while (*pstr1 == *pstr2)
{
pstr1++;
pstr2++;
}
if (*pstr1 > *pstr2)
return true;
else
{
return false;
}
}
(4)>=运算符重载
bool String::operator>=(const String& s)
{
if (*this > s || *this == s)
return true;
return false;
}
(5)==运算符重载
bool String::operator==(const String& s)
{
char* pstr1 = _str;
char* pstr2 = s._str;
while ((*pstr1 == *pstr2) && (*pstr1 != '\0' || *pstr2 != '\0'))
{
pstr1++;
pstr2++;
}
if (*pstr1 == *pstr2)
return true;
else
return false;
}
(6)<运算符重载
bool String::operator<(const String& s)
{
if (!(*this >= s))
return true;
return false;
}
(7)<=运算符重载
bool String::operator<=(const String& s)
{
if (*this < s || *this == s)
return true;
return false;
}
(8)!=运算符重载
bool String::operator!=(const String& s)
{
if (*this == s)
return false;
return true;
}
基本实现模拟String类的操作就是这些,其他简单的操作见源代码。
源代码(github):
https://github.com/wangbiy/C-/tree/master/test_2019_9_9_1