string模拟实现
前面我们已经对string进行了简单的介绍,只要会用各个函数即可,下面是string的模拟实现。
在这里我们可以定义一个自己的命名空间,防止与库里的string造成冲突。
namespace haifan
{
class string
{
public:
.....
private:
char* _str;
size_t _size;
size_t _capacity;
};
}
构造函数和析构函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JhE4Trrp-1690193767195)(C:\Users\Lenovo\AppData\Roaming\Typora\typora-user-images\image-20230724170813742.png)]
这是库里的几种构造函数,在这里,我只实现了一种。
如果在传参的时候我们是以haifan::string a
的形式或者是以 haifan::string b("123")
我们可以考虑用 string(const char* str = "")
这样的形式来写构造函数,当没有参数的时候,就用str的缺省值,有参数就用传递的参数。
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
memcpy(_str, str, _size + 1);
}
析构函数很简单,把_str释放掉,在置为空指针即可
~string()
{
delete[] _str;
_str = nullptr;
_capacity = _size = 0;
}
begin和end
begin和end分别由两种写法,一种是带const的,一种是不带的。
typedef char* iterator;
typedef const char* const_iterator;
const_iterator begin() const
{
return _str;
}
const_iterator end() const
{
return _str + _size;
}
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
reserve和resize
reserve是为字符串预留空间的一个函数,并且只有一个参数,表示要为字符串预留多少空间,如果n要比capacity小,可以不做处理,反之,要为字符串预留n + 1的空间,因为要放一个\0
void reserve(size_t n)
{
if (_capacity < n)
{
//注意还有一个\0
char* tmp = new char[n + 1];
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
resize是用字符c来填充多出的元素空间。在改变元素个数时,如果是增多,可能会改变底层容量的大小,如果减少,底层空间总大小不变。
void resize(size_t n, char c = '\0')
{
if (_capacity > n)
{
_str[n] = '\0';
_size = n;
}
else
{
reserve(n);
for (int i = _size; i < n; i++)
{
_str[i] = c;
}
_str[n] = '\0';
_size = n;
}
}
push_back和append
push_back是在字符串的尾部,添加一个字符—尾插,但在进行插入的时候,要注意空间是否足够,对此进行一个检查即可。
在插入字符后,别忘了在将\0进行一次尾插
void push_back(char ch)
{
if (_size + 1 > _capacity)
{
if (_capacity == 0)
{
reserve(4);
}
else
reserve(2 * _capacity);
}
_str[_size++] = ch;
_str[_size] = '\0';
}
append是将一个字符串进行尾插,同样,要先进行空间检查。如果空间不足,将空间开辟到跟尾插后的字符串一样的大小即可。
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
memcpy(_str + _size, str, len + 1);
_size += len;
}
c_str
c_str是将string类以C字符串的形式返回。
const char* c_str() const
{
return _str;
}
empty,size,capacity,clear
empty用于判断string是否为空。
bool empty() const
{
return _capacity == 0;
}
size用于判断string的长度
size_t size() const
{
return _size;
}
capacity用于判断string当前的容量
size_t capacity() const
{
return _capacity;
}
clear用于清空string,但是capacity并不会被清0
void clear()
{
_size = 0;
_str[_size] = '\0';
}
拷贝构造和赋值
拷贝构造设计到了空间的开辟,要用深拷贝,浅拷贝可能会造成两个string共用同一块地址。
string(const string& str)
{
_str = new char[str._capacity + 1];
_size = str._size;
_capacity = str._capacity;
memcpy(_str, str._str, str._capacity + 1);
}
赋值在这里,只实现了两种情况,一种是 string a = b
另一种是 string a = "abcd"
,前者利用拷贝构造先创建一个临时变量,然后用swap将*this和tmp进行交换,从而实现=的目的。后者是开辟了一块空间,将str里的内容,拷贝到tmp里面,然后将_str=tmp。
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
string& operator=(const string& s)
{
if (this != &s)
{
string tmp(s);
swap(tmp);
return *this;
}
}
string& operator= (const char* str)
{
char* tmp = new char[strlen(str) + 1];
memcpy(tmp, str, strlen(str) + 1);
_size = strlen(str);
_capacity = strlen(str) + 1;
delete[] _str;
_str = tmp;
return *this;
}
+=和比较大小
加等于一个字符,其实就是尾插
string& operator+= (char c)
{
push_back(c);
return *this;
}
加等于一个字符串其实就是append
string& operator+= (const char* str)
{
append(str);
return *this;
}
用于比较的代码很好写,只要写出来一个,其他的都可以复用。
比如小于
两种情况 两个string的长度相同或者不相同,如果相同直接memcpy进行字符串的比较即可,如果不相同,看哪个string短,先比短的部分,如果短的部分都相同,则string长的大,如果短的部分不相同,在比较的时候做出判断即可。
bool operator<(const string& s)
{
if (s._size == _size)
{
return memcpy(_str, s._str, _size);
}
else
{
if (_size > s._size)
{
for (int i = 0; i < s._size; i++)
{
if (_str[i] > s._str[i])
{
return false;
}
}
return false;
}
else
{
for (int i = 0; i < _size; i++)
{
if (_str[i] > s._str[i])
{
return false;
}
}
return true;
}
}
//int ret = memcmp(_str, s._str, _size < s._size ? _size : s._size);
//return ret == 0 ? _size < s._size : ret < 0;
}
一个好理解的写法,一个两行的写法
写出来一个,其他的都可以以<为基础来写
bool operator<=(const string& s)
{
if (*this < s || *this == s)
{
return true;
}
return false;
}
bool operator>(const string& s)
{
return !(*this <= s);
}
bool operator>=(const string& s)
{
return !(*this < s);
}
bool operator==(const string& s)
{
return memcpy(_str, s._str, _size) == 0 && _size == s._size;
}
bool operator!=(const string& s)
{
return !(*this == s);
}
[]重载
string类可以像字符串一样,用下表来访问,这是因为把[]进行了重载
char& operator[] (size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[] (size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
insert和erase
在第pos位置上插入一个字符,只需要把pos和pos后面的数据向后移动一位即可。
void insert(size_t pos,char c)
{
assert(pos < _size);
if (_size + 1 > _capacity)
{
reserve(2 * _capacity);
}
size_t tail = _size;
size_t head = pos;
while (tail >= pos && tail != npos)
{
_str[tail] = _str[tail - 1];
tail--;
}
_str[pos - 1] = c;
++_size;
_str[_size] = '\0';
}
在pos位置上插入一个字符串,将pos和pos之后的数据,向后移动字符串的长度即可。
void insert(size_t pos, const char* str)
{
assert(pos < _size);
const size_t len = strlen(str);
if (len + _size > _capacity)
{
reserve(len + _size);
}
size_t tail = _size;
while (tail >= pos && tail != npos)
{
_str[tail + len] = _str[tail];
tail--;
}
for (int i = 0; i < len; i++)
{
_str[i + pos] = str[i];
}
_size += len;
_str[_size] = '\0';
}
erase(size_t pos = 0, size_t len = npos)
pos是要删除的起始位置,len是要删除多少个,不写默认把pos之后的元素全部删除。
npos是 size_t npos = -1,因为size_t是无符号数,-1是一个正数且是一个及其大的值
void erase(size_t pos = 0, size_t len = npos)
{
assert(pos < _size);
if (len + pos > _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t tail = pos + len;
while (tail < _size)
{
_str[pos++] = _str[tail++];
}
_size -= len;
_str[_size] = '\0';
}
}
find查找
size_t find(char ch, size_t pos = 0)
从pos位置开始查找某个字符,如果找到了,返回下标,没找到,返回npos
size_t find(char ch, size_t pos = 0)
{
assert(pos < _size);
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)
查找子串,如果找到了,返回第一次找到子串的位置,反之,返回npos
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);
const char* ptr = strstr(_str + pos, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}