目錄
1. 遍歷
1.1. 下標+operator[ ]
1.2. c_str
1.3. 迭代器
1.4. 范圍for
2. 增
2.1. push_back
2.2. 重載+=(char ch)
2.3. appand
2.4. 重載+=(char* ch)
2.5. insert(任意位置插入)
2.5.1. 任意位置插入一個字符
2.5.2. 任意位置插入一個字符串
3. 刪
3.1. earse
情況一:len==npos說明要全部刪除完
情況二:并不全部刪除完
4. 查
4.1. 查找一個字符
4.2. 查找一個字符串
4.3. 字符串比較
5. 改
5.1. reserve
5.2. resize
情況一:resize的n大于當前字符串空間長度(注意是空間長度)
情況二:resize的n小于_capacity,但是大于_size
情況三:resize的n小于當前字符串長度
上一篇文章我們講解了string類的構造、拷貝構造、賦值及其模擬實現
下面是我們本篇文章的主要內容:
1. 遍歷
1.1. 下標+operator[ ]
char& operator[](size_t pos) {assert(pos < strlen(_str));return _str[pos]; }const char& operator[](size_t pos) const {assert(pos < strlen(_str));return _str[pos]; }int main() {string s1("hello world");for (int i = 0; i < s1.size(); ++i){cout << s1[i];}return 0; }
1.2. c_str
也就是說c_str返回的值指向該字符串并且包含“\0”的字符序列,使用c_str( )來打印字符串,當碰到“\0”時就停止(這是因為C/C++中字符串處理函數(如
printf
,?cout
,?strcpy
等)都遵循一個約定:將以null字符('\0')作為字符串的結束標志)。const char* c_str() const {return _str; }
1.3. 迭代器
迭代器:string的迭代器的底層其實就是一個char*的原生指針,所以使用string迭代器只需要像使用普通指針一樣即可。但是其他容器的底層不應該否是原生指針。
typedef char* iterator; typedef const char* const_iterator; iterator begin() {return _str; }iterator begin() const {return _str; } iterator end() {return _str + _size; }iterator end() const {return _str + _size; }
然后我們使用迭代器來進行字符串的遍歷:
int main() {s::string s1("Hello World");s::string::iterator it = s1.begin();while (it != s1.end()){cout << *it << "";it++;} }
PS:這個地方要注意的是要注意使用的是C++string類中的迭代器還是我們自定義類string中的迭代器。
1.4. 范圍for
for的使用:
for (auto e : s1) {cout << e << " "; }
其實范圍for的底層機制同樣是一個迭代器,我們可以通過下面的方式進行驗證,我們將迭代器給注釋掉,我們來看一下發生什么:
2. 增
2.1. push_back
可以看到,push_back的作用是將一個字符添加到原有字符串后面,具體步驟:
- 我們先要判斷時候需要擴容:如果size==capacity,說明滿了。擴容:開辟一個兩倍內存的新空間,然后原有字符串拷貝至新空間,釋放原空間
- 添加的新字符應該放在size的位置
- 然后處理“\0”即可
2.2. 重載+=(char ch)
但是我們在日常寫代碼中并不會經常使用push_back,而是使用 += ,所以我們來重載一下 += ,它的實現中也可以服用push_back:
string& operator+=( char ch) {push_back(ch);return *this; }
2.3. appand
這里可以注意到,前面兩個接口都是將單個字符添加到已有字符串結尾,而這個接口是將一個字符串添加到原來的字符串,步驟如下:
- 首先需要考慮要不要擴容,而且這里的擴容不能只是簡單地將空間變為兩倍,因為這樣并不能保證空間足夠。應該是將原字符串長度和新添加字符串長度相加,這樣得到的空間就一定能滿足要求,這里我們使用reserve函數(見下面的5.1)
- 然后更新 _size
void append(const char* str){size_t len = _size + strlen(str);if (len > _capacity){reserve(len);}strcpy(_str + _size, str);_size = len;//insert(_size, str);}
2.4. 重載+=(char* ch)
與單個字符的重載復用push_back一樣,復用appand接口:
string& operator+=(const char* str){append(str);return *this;}
2.5. insert(任意位置插入)
2.5.1. 任意位置插入一個字符
- 首先判斷pos位置是否合法
- 然后判斷是否需要擴容
- 然后從結尾的"\0"開始,從后往前一次向后移動一位,到pos為止
- 插入新字符,然后更新_size
string& insert(size_t pos, char ch) {assert(pos <= _size);if (_capacity == _size){reserve(_capacity == 0 ? 4 : _capacity * 2);}size_t end = _size + 1;while (end > pos){_str[end - 1] = _str[end];--end;}_str[pos] = ch;++_size;return *this; }
2.5.2. 任意位置插入一個字符串
- 插入位置是否合法
- 判斷是否需要擴容,這里需要擴大到原字符串長度+插入字符串長度
- 將原字符串包括pos在內的后面所有字符(包括"\0")往后移動len=strlen(ch)位
- 然后將新字符串插入,然后更新_size
string& insert(size_t pos, const char* str){assert(pos <= _size);size_t len = strlen(str);if (len == 0){return *this;}if (_size + len > _capacity){reserve(_size + len);}//挪動數據size_t end = _size + len;//while(end >= end+len)while (end > pos+len-1){_str[end] = _str[end-len];--end;}//插入數據 size_t i = 0;while (i < len){_str[pos+i] = str[i];++i;}_size += len;return *this;}
3. 刪
3.1. earse
我們看這里提到了一個參數npos:
所以可以知道,earse的作用就是從pos開始,往后len個長度,把這些字符刪去。
情況一:len==npos說明要全部刪除完
那其實我們邏輯上刪除它只需要將pos位置賦值"\0"即可:
情況二:并不全部刪除完
首先找到需要刪除的子字符串的后一位,將其定為begin,然后我們需要做的就是將begin后面的所有字符全部往前移動,覆蓋掉需要刪除的字符:
string& earse(size_t pos, size_t len = std::string::npos) {assert(pos < _size);if (len == std::string::npos || len + pos >= _size){_str[pos] = '\0';_size = pos;return *this;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];++begin;}_size -= len;return *this;} }
4. 查
4.1. 查找一個字符
find查找字符串,返回查到的第一個滿足的字符:
size_t find(char ch, size_t pos = 0) {for (; pos < _size; ++pos){if (_str[pos] == ch){return pos;}}return std::string::npos; }
4.2. 查找一個字符串
size_t find(const char* ch, size_t pos) {const char* p = strstr(_str + pos, ch);if (nullptr == p){return std::string::npos;}else{return p - _str;} }
4.3. 字符串比較
bool operator < (const string& s) {return strcmp(this->c_str(), s.c_str()) < 0; } bool operator == (const s::string& s) {return strcmp(this->c_str(), s.c_str()) == 0; } bool operator<=(const string& s) {return *this < s || *this == s; } bool operator>(const string& s) {return !(*this <= s); } bool operator>=(const string& s) {return !(*this < s); } bool operator!=(const string& s) {return !(*this == s); }
對于這段代碼有疑問的可以看下【C++】類和對象--類中6個默認成員函數(2) --運算符重載,這里涉及到成員函數隱藏this指針的問題。
5. 改
5.1. reserve
reserve的作用就是是個string的容量變成n:
- 如果n小于等于_capacity,則不進行擴容
- 否則開辟一個容量為n的char*,然后進行拷貝,注意釋放掉原有的內存
void reserve(size_t n) {if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;} }
5.2. resize
resize是將字符串大小修改為n。
resize和reserve的區別是:reserve只對空間進行處理,但是resize不僅對空間進行影響,而且會改變_size的值。
情況一:resize的n大于當前字符串空間長度(注意是空間長度)
看下面這個例子,假設空間長度為15,字符串長度為11:
也就是擴容了之后會使用字符串參數填充滿:
情況二:resize的n小于_capacity,但是大于_size
這種情況下不需要擴容,所以_capacity不會變:
情況三:resize的n小于當前字符串長度
這個時候只會保存原字符串的前n個字符:
代碼如下:
//擴空間+初始化 //刪除部分數據,保留前n個 void resize(size_t n, char ch = '\0') {if (n < _size){_size = n;_str[_size] = '\0';}else{if (n > _capacity){reserve(n);}for (size_t i = _size; i < n; ++i){_str[i] = ch;}_size = n;_str[_size] = '\0';}}
(本篇完)