string的模拟实现
目录
string
- string的主要功能
C++的string类是处理字符串的一个类,在C++中包含在string的头文件中。string类可以对string对象进行字符和字符串的一些操作,可以用字符串初始化一个string的对象。对string对象进行增删查改。计算字符串的大小和字符串存储空间的大小,可以使用接口获得字符串某一个位置的字符,或字符在哪个位置,和其他的一些主要功能。下面将会模拟实现。
模拟实现string
string所需要的成员函数
一个模拟的string的类需要有一个char*的指针来指向字符串的首地址,一个无符号int 的对象记录字符串长度,和另一个无符号int 的对象记录字符串所在空间的大小。
namespace imitstring
{
class string
{
public:
//接口
private:
char* _str;
size_t _size;
size_t _capactiy;
}
}
string的构造函数
string构造函数实例化一个对象:
传一个字符串
//1.
string(const char* str)//字符串是常量所以使用const来修饰
:_size(strlen(str))
,_capacity(_size)
{
_str = new char[_capacity + 1];//new与字符串一样大的空间
strcpy(_str, str);//把字符串的字符赋值到_str
}
传一个字符和一个所需字符个数
//2.
string(char ch, size_t n)
:_str(new char[n + 1])
,_size(n)
,_capacity(_size)
{
int i = 0;
while(i < n)
{
_str[i++] = ch;
}//循环把字符赋值到_str的空间
_str[i] = '\0';//要在结尾处加结束标识符
}
传一个对象进行实例化另一个对象
// 3.
string(const string& str)//拷贝构造
:_str(new char[str._capacity + 1])
, _size(str._size)
, _capacity(str._capacity)
{
strcpy(_str, str._str);
}
传一个字符串和一个在字符串大小范围内的n来初始前n个字符
// 4.
string(const char* str, size_t n)
:_str(new char[n + 1])
,_size(n)
,_capacity(n)
{
strncpy(_str, str, n);
_str[n] = '\0';
}
传一个字符串和字符串起始位置和结束位置中间的字符串
// 5.
string(const char* str, size_t n, size_t npos)
:_str(new char[npos - n + 1])//new一个npos到n的距离加1的空间
,_size(npos - n)
,_capacity(npos - n)
{
int i = 0;
while (n < npos)
{
_str[i++] = str[n++];
}
_str[i] = '\0';
}
初始化一个只有一个’\0’的
string()
:_str(new[1]{""})//存\0
,_size(0)
,_capacity(0)
{}
这些构造函数都在imitstring 命名空间里的string
void test_string1()
{
string st1("hello world");
string st2('x', 6);
string st3(st1);
string st4("hello world", 5);
string st5("hello world", 7, 10);
string st6;
}
string的运算符重载
赋值运算符重载
当有两个已经实例化的对象的时候,用一个对象去赋值另一个对象,就要使用运算符重载函数。首先需要把一个被赋值的对象的字符串数据进行delete,然后new 的空间和另一个对象字符串的空间一样大,再把字符串数据拷贝过来,把成员变量_size和_capacity浅拷贝。但char的str不能进行浅拷贝。因为两个对象的成员变量str不能指向同一块空间。最后返回一个this指针。
string& operator=(const string& str)
{
deldete[] _str;//释放_str空间内容
_str = new char[_capacity + 1];
strcpy(_str, str._str);
_size = str._size;
_capacity = str._capacity;
return *this;
}
首先,st1和st2调用构造函数进行初始化,然后调用运算符重载进行赋值,最后st2 得到 st1的拷贝。
[]运算符重载
char& operator[](size_t n){//可以通过下标修改,可读可写
assert(n < _size);
return _str[n];
}
const char& operator[](size_t n) const{//只可以读
assert(n < _size);
return _str[n];
}
这两个接口可以通过下标来进行访问。一可读可写,一个只可以读。
求string对象字符串容量的大小
封装求字符串长度的接口size()和容量大小的接口capacity()。
size_t size(){
return _size;
}
size_t capacity(){
return _capacity;
}
插入删除
插入数据,再尾部插入,我们先需要判断空间是否足够或者判断空间是否为0,如果空间不足,我们需要扩容。
插入一个字符
void push_back(char ch)
{
if (_size == _capacity)
{
_capacity = _capacity == 0 ? 4 : _capacity * 2;
char* tmp = new char[_capacity + 1];
strncpy(tmp, _str, _size);
delete[] _str;
_str = tmp;
}
_str[_size++] = ch;
_str[_size] = '\0';//结束
}
tmp申请的空间需要比容量多一个存’\0’。
如果原本有字符串,先判断空间是否足够,如果不够,进行2倍数扩容。如果是空,则先把_capacity赋值为4,然后再开空间。
插入字符串
插入对象的字符串
string& append(const string& s)
{
if (_size + s._size >= _capacity)
{
_capacity = (_size + s._size) * 2);
char* tmp = new char[_capacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
}
strncat(_str, s._str, s._size);
_size += s._size;
return *this;
}
首先需要判断空间的大小是否足够,如果足够,则直接再后面追加。如果空间不足,则开辟一个自身对象的字符串长度和需要插入对象的字符串长度的总和的2倍数。如果扩容了,就不会在原来的地址扩容,会异地扩容,所以我们需要开辟一个临时的char来开辟足够的空间。* 当我们开辟好后,先把原来的(_str)字符串赋值到新开辟的(tmp)空间,再把_str指向的tmp的地址 最后把另一个对象的字符串追加进去,把_size 的长度加上另一个对象字符串的长度。
插入一个常量字符串
string& append(const char* s)
{
if (_size + strlen(s) >= _capacity)
{
_capacity = _size + strlen(s);
char* tmp = new char[_capacity + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
}
strncat(_str, s, strlen(s));
_size += strlen(s);
_str[_size] = '\0';
return *this;
}
插入字符串常量与插入另一个对象的字符串原理相同。空间足够直接插入,空间不足异地开空间,把this的字符串赋值到新的空间,再把tmp赋值给_str,_str就指向新空间,然后再插入。下面是执行结果。
另一种插入方式(采用运算符重载)
string& operator+=(const string& s) {
this->append(s);
return *this;
}
string& operator+=(const char* s) {
this->append(s);
return *this;
}
string& operator+=(char c) {
this->push_back(c);
return *this;
}
通过复用append来进行追加,然后我们就可以用一个对象 += 另一个对象或者字符串或字符。
判断string是否为空
bool empty() const {
return _capacity == 0;
}
如果_capacity为0,说明string为空。
判断string大小
bool operator==(const string& s){
return strcmp(_str, s._str) == 0;
}//判断是否相等
bool operator!=(const string& s){
return !(*this == s);//复用==
}
bool operator<(const string& s){
return strcmp(_str, s._str) < 0;
}
bool operator>(const string& s){
return strcmp(_str, s._str) > 0;
}
bool operator>=(const string& s){
return !(*this < s);//复用<
}
bool operator<=(const string& s){
return !(*this > s);//复用>
}
当==函数重载 实现时,就可以复用 == 来实现 != ,取 ==函数重载的相反值。
和 <= ,< 和 >= 原理相同。
某个位置插入数据
某个位置插入一个字符
string& insert(char ch, size_t pos = 0)
{
if (_size == _capacity)
{
_capacity = _capacity == 0 ? 4 : 2 * _capacity;
char* tmp = new char[_capacity + 1];
strcpy(tmp, _str);
_str = tmp;
}
size_t end = _size + 1;
while (end > pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
return *this;
}
再某个位置插入某个字符,首先先判断空间是否足够,如果不足,就要另外开辟一个足够的空间,然后把原来的的内容拷贝过去,再把_str 的值赋值成tmp的。然后再从\0前面的第一个数据依次往后移动,直到再插入位置前停止,再把ch赋值到_str的pos位置。如果不需要扩容,就直接在原来的空间进行操作。
某个位置插入一个字符串
string& insert(const char* s, size_t pos = 0)
{
if (_size + strlen(s) > _capacity)
{
_capacity = _size + strlen(s);
char* tmp = new char[_capacity + 1];
strcpy(tmp, _str);
_str = tmp;
}
size_t end = _size + 1;
while (end > pos)
{
_str[end + strlen(s)] = _str[end];
end--;
}
strncpy(_str + pos, s, strlen(s));
_size += strlen(s);
return *this;
}
插入字符串,先计算当前字符串长度和需要插入的字符串长度,如果大于容量,则先扩容,开辟的空间为原来的字符串长度加字符串长度再加一。然后根据加入字符串的长度距离进行赋值,然后再追加要加入的空间再pos位置开始。
某个位置删除数据
string& erase(size_t pos, size_t len)
{
if (len == size_t(-1) || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
int begin = pos + len;
while (begin <= _size)
{
_str[begin - len] = _str[begin];
begin++;
}
_size -= len;
}
return *this;
}
如果删除的位置时结尾或者远超字符串长度,我们就可以直接再pos位置放\0,如果pos位置在长度内,我们只需要向前覆盖即可。
查找某个字母的位置
第一个字母的位置
size_t find_first_of(char ch, size_t pos = 0) const
{
while (pos < _size)
{
if (_str[pos] == ch)
break;
pos++;
}
return (size_t) -1;
}
pos表示的时哪个位置开始找,如果找到,则返回pos下标,如果找不到返回(size_t) -1。
字符串第一个字母的位置
size_t find_first_of(const char* s, size_t pos = 0) const
{
const char* p = strstr(_str + pos, s);
if (p)
{
return p - _str;
}
else
{
return size_t(-1);
}
}
查找字符串第一个字符与直接查找字符方法同理,但要通过指针相减来求值。
清除在空间的数据
void clear()
{
_str[0] = '\0';
_size = 0;
}
直接把第一个位置放\0,再把_size为0。
析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
先把空间的数据释放,把指针_str为nullptr,再把_size和_capacity为0;