string的模拟實作
- 1 先來介紹一下string
- 2 常見用法
-
- 2.1 成員函數
- 3 模拟實作
-
- 3.1 基礎接口實作
- 3.2 疊代器
- 3.3 operator[]實作
- 3.4 容量部分實作
- 3.5 擴容(insert、push_back、append、+=)
- 3.6 清理
- 3.7 關系運算
-
- 類内實作:
- 類外實作:
- 3.8 流運算(>>,<<,getline)
- 4 總結
1 先來介紹一下string
概述:string是C++标準庫裡面的一個重要部分,其主要工作是用于字元串處理。
2 常見用法
2.1 成員函數
具體實作原理詳見此網站。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnLmJGOyY2NwITM5kDMyAzN4AzYlRjM1YDZxkjYkNWNxgzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
3 模拟實作
class類的定義:
class string
{
public:
//具體函數實作,見下面部分給出
static const size_t npos;//類外定義
private:
char* _str;
size_t _size;
size_t _capacity;//存儲有效字元
};
const size_t string::npos = -1;
3.1 基礎接口實作
構造函數+拷貝構造函數+析構函數+指派構造函數:
// 基礎接口
//構造函數
string(const char* str = "")
{
_size = strlen(str);
_capacity = _size;
_str = new char[_capacity + 1];
strcpy(_str, str);
}
//現代寫法的swap成員函數 簡化程式代碼
void swap(string& s)
{
/*std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);*/
//域作用限定符,全局域
::swap(_str, s._str);
::swap(_size, s._size);
::swap(_capacity, s._capacity);
}
//s2(s1)拷貝構造
string(const string &s)
:_str(nullptr)
, _capacity(0)
, _size(0)
{
string temp(s._str);
// 多個交換
/*swap(_str, temp._str);
swap(_size, temp._size);
swap(_capacity, temp._capacity);*/
// 隐含 this->swap(temp);
swap(temp);//就近原則,使用的是局部的swap,目前類域找
}
string& operator=(string s)
{
/*swap(_str, s._str);
swap(_size, s._size);
swap(_capacity, s._capacity);*/
swap(s);
return *this;
}
//析構
~string()
{
delete[]_str;
_str = nullptr;
_size = _capacity = 0;
}
3.2 疊代器
// 疊代器
typedef char* iterator;
typedef const char* const_itertor;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size; //end位置是\0位置
}
const iterator begin() const
{
return _str;
}
const iterator end() const
{
return _str + _size;
}
3.3 operator[]實作
// &保證了 可讀可寫,出了作用域對象還在
char& operator[](size_t i)
{
assert(i < _size);//防止越界
return _str[i];
}
//隻能讀
const char& operator[](size_t i) const
{
assert(i < _size);
return _str[i];
}
3.4 容量部分實作
此部分主要實作的是size,capacity,判斷是否為空,reserve,resize接口。
這裡主要分析reserve接口和resize接口:
// capacity
size_t size()
{
return _size;
}
size_t size() const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return _size == 0;
}
//開空間 改變容量
void reserve(size_t n)
{
if (n > _capacity)
{
char* temp = new char[n + 1];
//strcpy(temp, _str);
strncpy(temp, _str,_size+1);
delete[]_str;
_str = temp;
_capacity = n;
}
}
//開空間,初始化,縮小,size也變換
void resize(size_t n, char ch = '\0')
{
//空間足
if (n < _size)
{
//縮小,插入\0,迫使其在n的位置終止
_str[n] = '\0';
_size = n;
}
else
{
//容量不夠,擴容
if (n > _capacity)
{
reserve(n);
}
// n<capacity
for (size_t i = _size; i < n; ++i)
{
_str[i] = ch;//插入字元
}
_str[n] = '\0';//末尾為結束标志
_size = n;//記錄容量
}
}
3.5 擴容(insert、push_back、append、+=)
首先分析如何insert一個字元:
其次,再次分析如何insert一個字元串:
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
//容量已滿
if (_size == _capacity)
{
size_t newcapacity = _capacity == 0 ? 16 : _capacity * 2;
reserve(newcapacity);//擴容
}
//移動pos位置及之後字元
//寫法一 不推薦
/* int end = _size;
while (end >= (int)pos) // 避免0 - 1加(int)
{
_str[end+1] = _str[end];
--end;
}
_str[pos] = ch;
_size++;
return *this;
*/
//寫法二 推薦
int end = _size + 1;
while (end > pos)
{
_str[end] = _str[end - 1];
--end;
}
//寫法3 someproblem
/*char* end = _str + _size;
while (end >= _str + pos)
{
*(end + 1) = *end;
--end;
}*/
_str[pos] = ch;
_size++;
return *this;
}
//pos位置之前 insert 字元串
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len > _capacity)
{
reserve(_size + len);
}
//空間足 挪動 *****分析界限問題*****
//分析實作辦法 一
/* size_t end = _size + len;
while (end >= (int)(pos + len))
{
_str[end] = _str[end - len];
--end;
}*/
//實作辦法 二
/* cout << "size: " << _size << endl;
cout << "_str[4]: " << _str[4] << endl;
cout << "_str[8]: " << _str[8] << endl;*/
size_t end = _size + len + 1;
//cout << "end:" << end << endl;
while (end > pos + len)
{
_str[end - 1] = _str[end - len - 1];//先從結束\0 開始向後移動
--end;
}
//cout << "end: "<< end << endl;
//指針辦法解決
/*char*end = _str + _size;
while (end >= _str + pos)
{
*(end + len) = *end;
--end;
}*/
strncpy(_str + pos, str, len);
_size += len;
}
//*****************************
//修改
void push_back(char ch)
{
//if (_size == _capacity)
//{
// //擴容
// reserve(2 * _capacity);
//}
//_str[_size] = ch;
//++_size;
//_str[_size] = '\0';//保證字元串終止
insert(_size, ch);
}
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);
}
//this 指針處理
string& operator+=(char ch)
{
push_back(ch);
return *this;//深拷貝
}
//+= 字元串
string& operator+=(const char* str)
{
append(str);
return *this;//深拷貝
}
3.6 清理
void clear()
{
_size = 0;
_str[_size] = '\0';
}
void erase(size_t pos, size_t len = npos)
{
assert(pos < _size);
//删完、删的len超過_size
if (len == npos || pos+len >= _size)
{
_str[pos] = '\0';//直接設為字元串終止
_size = pos;//修改大小
}
else//删除一小段
{
strcpy(_str + pos, _str + pos + len);//字元串向前覆寫移動
_size -= len;
}
}
//找一個字元
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* sub, size_t pos = 0)
{
const char* ret = strstr(_str + pos, sub);
if (ret == nullptr)
{
return npos;
}
else
{
return ret - _str;
}
}
3.7 關系運算
類内實作:
// 類内實作關系運算
//relational operators
bool operator<(const string& s)const
{
int res = strcmp(_str, s._str);
if (res < 0)
return true;
return false;
}
bool operator<=(const string& s)const
{
return !(*this > s);
}
bool operator>(const string& s)const
{
int res = strcmp(_str, s._str);
if (res > 0)
return true;
return false;
}
bool operator>=(const string& s)const
{
return !(*this < s);
}
bool operator==(const string& s)const
{
int res = strcmp(_str, s._str);
if (res == 0)
return true;
return false;
}
bool operator!=(const string& s)const
{
return !(*this == s);
}
類外實作:
實作辦法一:利用strcmp函數實作
bool operator ==(const string&s1, string &s2)
{
return strcmp(s1.c_str(), s2.c_str()) == 0;
}
bool operator !=(const string&s1, string &s2)
{
return !(s1 == s2);
}
bool operator <(const string&s1, string &s2)
{
return strcmp(s1.c_str(), s2.c_str()) < 0;
}
bool operator <=(const string&s1, string &s2)
{
return s1<s2 || s1 == s2;
}
bool operator >(const string&s1, string &s2)
{
return !(s1 <= s2);
}
bool operator >=(const string&s1, string &s2)
{
return !(s1 < s2);
}
實作辦法二:自己模拟實作比大小
bool operator>(const string&s1, const string&s2)
{
size_t i1 = 0, i2 = 0;
while (i1 < s1.size() && i2 < s2.size())
{
if (s1[i1] > s2[i2])
{
return true;
}
else if (s1[i1] < s2[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
if (i1 < s1.size())
{
return true;
}
else if (i2<s2.size())
{
return false;
}
else
{
return false;
}
}
bool operator==(const string& s1, const string& s2)
{
size_t i1 = 0, i2 = 0;
while (i1 < s1.size() && i2 < s2.size())
{
if (s1[i1] > s2[i2])
{
return false;
}
else if (s1[i1] < s2[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
if (i1 == s1.size() && i2 == s2.size())
{
return true;
}
else
{
return false;
}
}
3.8 流運算(>>,<<,getline)
ostream& operator<<(ostream& out, const string& s)
{
//方法一 :範圍for解決
for (auto ch : s)
{
out << ch;
}
//方法二
/*for (size_t i = 0; i < s.size(); ++i)
{
out << s[i];
}*/
return out;
}
istream& operator>>(istream& in, string&s)
{
s.clear();
//s.resize(0);//清理 == clear
char ch;
ch = in.get();
while (ch != ' ' && ch != '\n')
{
s += ch;
//in>>ch;
in.get(ch); //擷取
}
return in;
}
//Getline 操作
istream& getline(istream& in, string& s)
{
s.clear();
//s.resize(0);//清理 == clear
char ch;
ch = in.get();
while (ch != '\n')
{
s += ch;
//in>>ch;
in.get(ch); //擷取
}
return in;
}
4 總結
整體實作+測試代碼點選此處。
關于string類的一些東西,融彙貫通,多比較練習。