string类模拟实现
前言:今天我们用C++实现一个增删查改的string类
1.构造,拷贝构造,析构
定义私有成员变量:
private:
char* _str;
size_t _size; //已经有多少个有效字符
size_t _capacity; //能存多少个有效字符
static size_t npos;
1.1实现构造函数
string(const char* str="")
{
_size = strlen(str);
_capacity = _size; // '\0'不是有效字符
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//string(const char* str = "\0") 错误示范
//string(const char* str = nullptr) 错误示范
1.2 拷贝构造
深浅拷贝问题
浅拷贝,也称值拷贝,编译器只是将对象中的值拷贝过来,会出现多个对象共用同一份资源,当一个对象将那份空间释放掉,另一个对象不知道的时候会出现错误访问。
浅拷贝的问题:1.同一块空间会被析构两次 2.其中一个修改数据会影响另一个
string(const string& s)
:_str(new char[strlen(s._str)+1])
,_size(0)
,_capacity(0)
{
strcpy(_str,s._str);
_size = s._size;
_capacity = s._capacity;
}
1.3 析构函数
~string()
{
delete[] _str;
_str = nullptr; //释放后记得置空
_capacity = _size = 0;
}
2.运算符重载
当我们想比较两个string类的大小时,内置的运算符不适用,因此我们将自己实现运算符重载
由于运算符重载比较简单,不做过多阐述,直接将实现的代码写上
2.1 s1<s2
bool operator<(const string& s)
{
int ret = strcmp(_str, s._str); //比较两个字符串大小,比较ASII码值
return ret < 0;
}
2.2 s1==s2
bool operator==(const string& s)
{
int ret = strcmp(_str, s._str);
return ret == 0;
}
2.3 s1<=s2
bool operator<=(const string& s)
{
return *this < s || *this == s;
}
2.4 s1>s2
bool operator>(const string& s)
{
return !(*this <= s);
}
2.5 s1>=s2
bool operator>=(const string& s)
{
return !(*this < s);
}
2.6 s1!=s2
bool operator != (const string& s)
{
return !(*this == s);
}
3.增删查改
由于我们会在类外面遍历数组之类等操作会用到_size,_capacity,_str等私有成员变量,因此我们将它封装成函数,请看下面实现
size_t size() const
{
return _size;
}
size_t capacity() const
{
return _capacity;
}
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
const char* c_str()
{
return _str;
}
3.1 增加一个字符或者字符串
插入的时候可能会出现空间不够的时候,因此我们要增容,由于经常会使用增容,防止出现代码冗长,因此我们将增容用一个函数实现,方便调用。
void reserve(size_t n)
{
if (n > _capacity)
{
char* newstr = new char[n + 1];
strcpy(newstr, _str);
delete[] _str;
_str = newstr;
_capacity = n;
}
}
push_back :尾插一个字符
void push_back(char ch)
{
if (_size == _capacity) //判断是否需要增容
{
size_t newcapacity =_capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0'; //必须加一个\0 ,否则出现内存泄露
}
append : 尾插字符串
void append(const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
size_t newcapacity = _size + len;
reserve(newcapacity);
}
strcpy(_str+_size, str);
_size += len;
}
由于插入字符和字符串会用到两个不同的函数,因此为了方便我们用"+="来实现
string& operator+=(char ch)
{
this->push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
this->append(str);
return *this;
}
上述代码可以看出“+=”的底层实现仍然是调用push_back和append
3.2 删除
从pos位置,删除len长度的字符
void erase(size_t pos, size_t len = npos)
{
if (len >= _size - pos) //判断是否删除pos位置后所有字符
{
_str[pos] = '\0';
_size = pos;
}
else
{
while (pos <= _size)
{
_str[pos] = _str[pos + len];
pos++;
}
_size -= len;
}
}
3.3 查找
在已知的字符串从pos位置查找所需要的字符或者字符串,返回它的位置,没知道则返回-1
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos;i < _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
char* p = strstr(_str, str); //寻找字符串里的子串
if (p == nullptr)
{
return npos;
}
else
return p - _str; //两个指针之间的个数
}
3.4 插入
在pos位置插入一个字符或者字符串
void insert(size_t pos, char ch)
{
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 2 : _capacity * 2;
reserve(newcapacity);
}
size_t end = _size;
while (end >= pos)
{
_str[end + 1] = _str[end];
end--;
}
_str[pos] = ch;
_size++;
}
void insert(size_t pos, const char* str)
{
size_t len = strlen(str);
if (_size + len > _capacity)
{
size_t newcapacity = _size + len;
reserve(newcapacity);
}
size_t end = _size;
while (end >= pos)
{
_str[end + len] = _str[end];
end--;
}
for (size_t i = 0; i < len; i++)
{
_str[pos++] = str[i];
}
_size += len;
}