目录
前言
好长时间没有更新博客了,本来想着分批次更新string的相关函数功能的,最后思来想去写一篇模拟实现string或许效果好得多,对于理解string或许会更好一些!
string
其实string是一个官方实现的类,我们通过调用类的成员函数就可以完成对字符串的相关操作!与前面所介绍的类相似,只不过C++是官方的将string类作为一个类型供我们使用的!
笔者在模拟实现的时候按照以下方式进行模拟:
private:
char* _str;
size_t _capacity;//容量
size_t _size;//有效字符个数
const static size_t npos = -1;
在下面模拟string实现时,笔者不会去介绍函数的功能,更多的可以去参考C++的功能文档,对于函数中某些函数语句会注释讲解,当然也只是对于比较常用的函数进行模拟,这篇博客笔者也会不断更新,确保将常用的函数接口大多数进行模拟!
构造函数 析构函数 拷贝函数
1.构造函数
string(const char* s)
{
size_t n = strlen(s);
_str = new char[n + 1];
_capacity = _size = n;
strcpy(_str, s);
}
string()
{
_str = new char[1];
_str[0] = '\0';
_capacity = _size = 0;
}
//""中是\0
string(const char* str = "")
{
size_t len = strlen(str);
_str = new char[len + 1];
_size = _capacity = len;
strcpy(_str, str);
}
//此种版本更加好一些,包含了前两种版本
2.析构函数
~string()
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
3.拷贝函数
//拷贝构造函数
string(const string& str)
{
_str = new char[str._capacity + 1];
_capacity = str._capacity;
_size = str._size;
strcpy(_str, str._str);
}
//赋值重载函数
string& operator=(const string& str)
{
if (&str != this)
{
char* temp = new char[str._capacity + 1];
strcpy(temp, str._str);
delete[]_str;
_str = temp;
_capacity = str._capacity;
_size = str._size;
}
return *this;
}
常用函数模拟
reverse
//扩容,预留空间
void reserve(size_t n)
{
if (_capacity < n)
{
char* temp = new char[n + 1];//多扩增1个空间用于存储'\0'
strcpy(temp, _str);
delete[] _str;
_str = temp;
_capacity = n ;
}
}
resize
//调整size
void resize(size_t n, char ch= '\0')
{
if (n > _size)
{
reserve(n);
for (int i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
else
{
_str[n] = '\0';
_size = n;
}
}
append
/插入字符串(尾部插入)
void append(const char* str)
{
size_t len = strlen(str);
if (_capacity < _size + len)
{
reserve(_size + len);
}
strcpy(_str + _size, str);
_size += len;
}
push_back
//插入字符(尾部插入)
void push_back(const char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
+=
//重载+=
//插入字符
string& operator+=(const char ch)
{
push_back(ch);
return *this;
}
//插入字符串
string& operator+=(const char* str)
{
append(str);
return *this;
}
erase
//删除
string& erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
//删除长度大于size-pos,即剩余字符的长度也就意味着全部删除
if (len >= _size - pos || len == npos)
{
_str[pos] = '\0';
_size = pos;
}
else
{
//对删除内容进行覆盖
strcpy(_str + pos, _str + len + pos);
_size = _size - len;
}
return *this;
}
find
//查找
//查找字符
size_t find(char ch, size_t pos = 0) const
{
assert(pos < _size);
for (int i = pos; i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
//查找字符串
size_t find(const char* str, size_t pos = 0) const
{
assert(pos < _size);
char* ptr = strstr(_str, str);
if (ptr == nullptr)
{
return npos;
}
return ptr - _str;
}
[ ]
//重载[]
//可读可写
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
//只读[]
const char& operator[](size_t pos) const//修饰this指针
{
assert(pos < _size);
return _str[pos];
}
insert
//插入
//插入字符
string& insert(const char ch, size_t pos)
{
assert(pos < _size);
//判断容量
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 4 : _capacity * 2;
reserve(newcapacity);
}
//先移动位置
for (int i = size() + 1; i > pos; --i)
{
_str[i] = _str[i-1];
}
_str[pos] = ch;
_size++;
return *this;
}
//插入字符串
string& insert(const char* str, size_t pos)
{
assert(pos < _size);
size_t len = strlen(str);
//插入长度+有效字符长度大于容量扩容
if (_size + len > _capacity)
{
reserve(_size+len);
}
size_t end = _size + len;
size_t range = size();
//方法一(int强制类型转换)
//隐式类型转换
/*while ((int)range >= (int)pos)
{
_str[end--] = _str[range--];
}*/
//方法二
//将需要后移的元素移走后,此时end的位置就是插入字符串的末尾位置
while (end > len + pos - 1)
{
_str[end--] = _str[range--];
}
//本质思想其实都一样,唯一不同的就是while控制条件的不同
strncpy(_str + pos, str, len);
//防止中间插入字符串将'\0'插入,所以使用strnpy
_size += len;
return *this;
}
c_str
const char* c_str() const
{
return _str;
}
其他
//返回capacity
size_t capacity() const
{
return _capacity;
}
//返回size
size_t size() const
{
return _size;
}
//清空字符串
void clear()
{
assert(_size);
_str[0] = '\0';
_size = 0;
}
重载>> 和<<
//重载<<
ostream& operator<<(ostream& out, const string& str)
{
for (int i = 0; i < str.size(); i++)
{
out << str[i];
}
return out;
}
//重载>>
istream& operator>>(istream& in, string& str)
{
str.clear();
//重复+=导致大量reserve,效率低下
/*char ch = in.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = in.get();
}*/
char buff[128] = { '\0' };
char ch = in.get();
int i = 0;
while (ch != ' ' && ch != '\n')
{
if (i == 127)
{
str += buff;
i = 0;
}
buff[i++] = ch;
ch = in.get();
}
//i并不等于127,此时退出while循环
if (i > 0)
{
buff[i] = '\0';
str += buff;
}
return in;
}
Ending
当然关于string的相关函数接口还有很多,笔者也只是实现了很少的一部分,但是这一部分是比较常用的!接下来本篇博客或许会不断更新如果有较为重要的函数的话还是会进行模拟实现的!