string模拟实现(C++)
目录
1. string成员变量
2. 构造和析构
(1)构造函数
(2)拷贝构造
(3)析构函数
3. reserve和resize
4. find
5. insert和erase
6. push_back和append
7. 重载
(1)方括号重载
(2)比较大小
(3)流提取和流插入
(4)尾插
(5)赋值运算符
8. clear
【代码汇总】
string是C++里面对于字符串处理非常方便的容器,不仅支持方括号加下标的访问,还内置各种接口,用起来嘎嘎爽。
前置注意事项:
在模拟实现的时候,我们需要自己开一块命名空间,避免跟库里的string发生冲突
需要对类和对象的相关知识有一定的了解,可翻阅我之前的文章
1. string成员变量
有点类似顺序表的实现(可看这篇文章),有一个size(数组大小)和capacity(数组容量),而string的本质是字符数组,有char*来记录数据
namespace myString { class string { public: typedef char* iterator; typedef const char* const_iterator; // 提供接口来访问第一个元素和最后一个元素的下一位 iterator begin() { return _str; } const_iterator begin() const { return _str; } iterator end() { return _str + _size; } const_iterator end() const { return _str + _size; } const_iterator begin() const { return _str; } const_iterator end() const { return _str + _size; } // 获取大小和容量的接口 // 这里要用const修饰成员函数,保证const对象也可以调用 size_t size() const { return _size; } size_t capacity() const { return _capacity; } bool empty() const { return _size == 0; } //获取C类型的字符串 const char* c_str() const { return _str; } private: char* _str = nullptr; size_t _size = 0; size_t _capacity = 0; public: static const size_t npos; }; }
【补充】这里的npos一般赋值为-1,用来表示字符串的末尾,因为-1在无符号整型中为2^32-1,大小约为4G,所以一般不会达到,所以用来表示末尾,通常作缺省值使用。或者在查找不到需要的字符(字符串)时作为返回值表示查找失败。
2. 构造和析构
(1)构造函数
string(const char* str = "") :_size = strlen(str) { _capacity = _size; _str = new char[_capacity + 1]; strcpy(_str, str); }
(2)拷贝构造
注意要新开一块空间再将数据拷贝过来,否则两个指针指向同一块空间时,当其中一个发生改变另一个也会变,而且在析构时同一块空间析构两次会引发程序的崩溃,而这也就是我们常说的浅拷贝。
而深拷贝就是先开一块新的空间,再将数据依次拷过去,可以有效地避免上述情况的发生。
// 版本一 string(const string& s) { _str = new char[s.capacity() + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; }
版本二复用了(1)构造函数,同时写了一个swap函数
Q:有小伙伴就问了,为什么不直接用库里的swap呢?
可以看到这里要调用一次拷贝构造,两次赋值,还得调用析构把c析构掉
代价太大,所以我们不如自己实现一个
这里要用std限定住范围是因为swap会先从就近的swap开始找,但是由于参数个数不匹配,所以就会报错。
// 版本二 void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } string(const string& s) { string tmp(s._str); swap(tmp); }
(3)析构函数
~string() { delete[] _str; _str = nullptr; _size = _capacity = 0; }
3. reserve和resize
【区分reserve和resize】
reserve:只是开空间,而且如果请求的空间大小小于已有大小,不会缩容
resize:若扩大,并赋值给对应的插入值,默认为'\0';若给的大小小于size则缩容到对应大小
void reserve(size_t n) { if(n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } void resize(size_t n, char c = '\0') { if(n pos) { _str[end] = _str[end - 1]; end--; } _str[pos] = c; _size++; } void insert(size_t pos, const char* str) { assert(pos _capacity) reserve(_size + len); size_t end = _size + len; while(end > pos + len - 1) { _str[end] = _str[end - len]; end--; } strncpy(_str + pos, str, len); _size += len; } // 删除pos位置上的元素,并返回该元素的下一个位置 void erase(size_t pos, size_t len = npos) { aasert(pos = _size) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } }
6. push_back和append
老思路,扩容扩二倍;记得加回 '\0'
void push_back(char c) { if (_size == _capacity) reserve(_capacity == 0 ? 4 : _capacity * 2); _str[_size] = c; _str[++_size] = '\0'; }
【版本二】复用insert
void push_back(char c) { insert(_size, c); }
append:不要忘记扩容!!因为传进来的是c类型字符串,所以直接使用strlen
void append(const char* str) { size_t len = strlen(str); if (_size + len > _capacity) reserve(_size + len); strcpy(_str + _size, str); _size += len; }
7. 重载
(1)方括号重载
char& operator[](size_t index) { assert(index(2)比较大小
注意strcmp函数是c语言的接口,需要转成const char*才能传参
bool operator==(const string& s1, const string& s2) { int ret = strcmp(s1.c_str(), s2.c_str()); return ret == 0; } bool operator _capacity) reserve(_size + len); strcpy(_str + _size, str); _size += len; } string& operator+=(const char* str) { append(str); return *this; } void clear() { _str[0] = '\0'; _size = 0; } void swap(string& s) { std::swap(_str, s._str); std::swap(_size, s._size); std::swap(_capacity, s._capacity); } const char* c_str() const { return _str; } / // capacity size_t size()const { return _size; } size_t capacity()const { return _capacity; } bool empty()const { return _size == 0; } void resize(size_t n, char c = '\0') { if (n _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[] _str; _str = tmp; _capacity = n; } } / // access char& operator[](size_t index) { assert(index pos) { _str[end] = _str[end - 1]; end--; } _str[pos] = c; _size++; } void insert(size_t pos, const char* str) { assert(pos _capacity) reserve(_size + len); size_t end = _size + len; while (end > pos + len - 1) { _str[end] = _str[end - len]; end--; } strncpy(_str + pos, str, len); _size += len; } // 删除pos位置上的元素,并返回该元素的下一个位置 void erase(size_t pos, size_t len = npos) { assert(pos = _size) { _str[pos] = '\0'; _size = pos; } else { strcpy(_str + pos, _str + pos + len); _size -= len; } } private: char* _str; size_t _capacity; size_t _size; public: static const size_t npos; }; const size_t string::npos = -1; void swap(string& x, string& y) { x.swap(y); } bool operator