C++引入了面向对象的思想,相比于C语言,一个类能更好地对一些数据结构进行管理和操作。
在C语言中,我们使用字符数组和string.h中的库函数来维护一个字符串,数据与方法分离不说,由于底层的空间是自己维护的,稍不留神就可能造成越界
在C++中,基于面向对象的思想,用来管理字符串的string类便应运而生,从本质上讲,string类就是一个被封装了的字符数组
目录
string的文档介绍:
typedef basic_string<char, char_traits, allocator> string
也就是说,我们常提到的string其实是basic_string类模板的使用单字节char的实例化并typedef成了string
,basic_sring亦可以用双字节的wchar_t实例化,用以处理ascii码无法表示的其他文字,如中文等
函数名 | 功能 |
---|---|
string() | 构造空的string类对象,即空字符串 |
string(const char* s) | 用常量字符串来构造string类对象 |
string(size_t n, char c) | string类对象中包含n个字符c |
string(const string&s) | 拷贝构造函数 |
~string() | 析构函数 |
operator= | 赋值重载,将一个string对象赋值给另一个string对象 |
多种构造函数的使用:
void Teststring() { string s1; // 构造空的string类对象s1 string s2("hello world"); // 用C格式字符串构造string类对象s2 string s3(s2); // 拷贝构造s3 }
接口的模拟实现:
//全缺省带参默认构造函数 basic_string(const T* s = "") :_size(strlen(s)) ,_capacity(strlen(s)) { _str = new T[_size + 1]; strcpy(_str, s); }
//析构函数 ~basic_string() { delete[] _str; _str = nullptr; _size = 0; _capacity = 0; }
//拷贝构造(现代写法) basic_string(const basic_string& str) : _str(nullptr) { if (this != &str) { basic_string tmp(str._str); swap(tmp); } }
//赋值重载(现代写法2) basic_string& operator=(basic_string str) { swap(str); return *this; }
接口名称 | 接口作用 |
---|---|
size() | 返回字符串有效字符长度(’\0’之前的字符长度) |
length() | 同size() |
capacity() | 返回当前分配给字符串的空间大小 |
empty() | 判断字符串是否为空字符串 |
reserve(n) | 重置capacity的大小,为字符串预留空间 |
resize(n, c) | 重置有效字符,将有效字符的个数该成n个,多出的空间用字符c填充 |
clear() | 清空有效字符,将字符串变为空字符串 |
注意:
接口的模拟实现:
//size()接口 size_t size() const{ return _size; }
//length()接口 size_t length() const{ return _size; }
//capacity()接口 size_t capacity() const { return _capacity; }
// 判空 bool empty() const { return _size == 0; }
void reserve(int new_capacity) { T* tmp = new T[new_capacity + 1];//多开的一个T的大小用来存'\0' strcpy(tmp, _str); delete[] _str; _str = tmp; tmp = nullptr; _capacity = new_capacity; }
void resize(size_t n, T c = '\0') { if (n <= _size) { _str[n] = '\0'; _size = n; } else { if (n > _capacity) { reserve(n); } for (size_t i = _size; i < n; i++) { _str[i] = c; } _str[n] = '\0'; _size = n; } }
// 清除 void clear() { _str[0] = '\0'; _size = 0; }
接口名称 | 接口作用 |
---|---|
operator[ ] | 返回pos位置的字符 |
begin() | 返回第一个有效字符的迭代器 |
end() | 返回最后一个字符下一个位置的迭代器 |
rbegin() | 返回最后一个有效字符的迭代器 |
rend() | 返回第一个字符的前一个位置的迭代器 |
注意:在string类里,迭代器就是字符指针
注意:auto it = rbegin(); it++是让it指向前面一个位置的地址
接口的模拟实现:
//重载[] T& operator[](size_t pos) { assert(pos < _size); return _str[pos]; } const T& operator[](size_t pos) const{ assert(pos < _size); return _str[pos]; }
typedef T* iterator; iterator begin() { return _str; } iterator end() { return _str + _size; }
接口名称 | 接口作用 |
---|---|
push_back() | 在字符串后尾插字符c |
append() | 在字符串后尾插字符串s |
operator+= | 在字符串后尾插字符c/字符串s |
insert() | 在字符串下标为pos的位置开始插入n个字符c/字符串s |
erase() | 在字符串下标为pos的位置开始删除n个字符c/字符串s |
swap() | 交换两个类对象 |
接口的模拟实现:
//push_back尾插一个字符 void push_back(const T c) { if (_size == _capacity) { size_t new_capacity = _capacity == 0 ? 4 : 2 * _capacity; reserve(new_capacity); } _str[_size] = c; _size++; _str[_size] = '\0'; } // 复用push_back 重载+=字符 void operator+=(const T c) { push_back(c); }
//append尾插字符串 void append(const T* str) { size_t len = strlen(str); if (_size + len > _capacity) { reserve(_size + len); } strcpy(_str + _size, str); _str[_size + len] = '\0'; _size += len; } //复用append,重载+=字符串 void operator+=(const T* str) { append(str); }
//插入字符 basic_string& insert(size_t pos, const T c) { assert(pos <= _size); if (_size == _capacity) { size_t new_capacity = _capacity == 0 ? 4 : 2 * _capacity; reserve(new_capacity); } _size++; size_t end = _size + 1; while (end > pos) { _str[end] = _str[end - 1]; end--; } _str[pos] = c; return *this; } //插入字符串 basic_string& insert(size_t pos, const T* s) { assert(pos <= _size); size_t len = strlen(s); if (len + _size > _capacity) { reserve(len + _size); } size_t end = _size + len; while (end - len + 1 > pos) { _str[end] = _str[end - len]; end--; } _size += len; memcpy(begin() + pos, s, len * sizeof(T)); return *this; }
iterator erase(size_t pos, size_t len = npos) { assert(pos < _size); if (pos + len >= _size) { _str[pos] = '\0'; } else { strcpy(begin() + pos, begin() + pos + len); } _size = strlen(_str); return begin() + pos; }
void swap(basic_string& str) { ::swap(_size, str._size); ::swap(_capacity, str._capacity); ::swap(_str, str._str); }
接口名称 | 接口作用 |
---|---|
find() | 从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置,找不到返回npos |
rfind() | 从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置,找不到返回npos |
c_str() | 以常量字符串的形式返回string类对象中的字符指针 |
substr() | 从pos位置开始往后截取长度为n的子串s,返回新的子串的类;n默认为npos |
接口的模拟实现:
size_t find(const T c, size_t pos = 0) const { assert(pos < _size); for (size_t i = pos; i < _size; i++) { if (_str[i] == c) { return i; } } return npos; } size_t find(const T* s, size_t pos = 0) const { assert(pos < _size); T* ret = strstr(_str + pos, s); if (ret == nullptr) { return npos; } else { while (*ret != _str[pos]) { pos++; } return pos; } }
const T* c_str() const { return _str; }
basic_string substr(size_t pos, size_t n = npos) { assert(pos < _size); basic_string tmp; tmp.resize(_size); if (pos + n >= _size || n == npos) { strcpy(tmp._str, _str + pos); _size -= pos + 1; } else { strcpy(tmp._str, _str + pos); *(tmp._str + n) = '\0'; _size = n; } cout << _size << endl; return tmp; }
接口名称 | 接口作用 |
---|---|
npos | static const size_t npos = -1; |
接口的模拟实现:
static const size_t npos = -1;
接口名称 | 接口作用 |
---|---|
operator+ | 返回一个2个字符串相加的新构造的字符串对象 |
operator<< | 输出所有有效字符 |
operator>> | 输入,遇到空格或换行终止 |
getline() | 输入,遇到换行终止 |
接口的模拟实现:
//operator+ basic_string operator+(const basic_string& str) { basic_string ret(*this); ret += str._str; return ret; } basic_string operator+(const T* s) { basic_string ret(*this); ret += s; return ret; }
ostream& operator<<(ostream& out, const basic_string<char>& str) { for (size_t i = 0; i < str.size(); i++) { out << str[i]; } return out; }
istream& operator>>(istream& in, basic_string<char>& str) { str.clear(); char ch; ch = in.get(); while (ch != ' ' && ch != '\n') { str += ch; ch = in.get(); } return in; }
istream& getline(istream& in, basic_string<char>& str) { str.clear(); char ch; ch = in.get(); while (ch != '\n') { str += ch; ch = in.get(); } return in; }
接口名称 | 接口作用 |
---|---|
< | 小于 |
== | 等于 |
!= | 不等于 |
> | 大于 |
>= | 大于等于 |
<= | 小于等于 |
接口模拟实现:
bool operator<(const basic_string& str) { size_t end = 0; while (end < _size && end < str._size) { if (_str[end] > str._str[end]) { return false; } else if(_str[end] < str._str[end]){ return true; } end++; } if (end == str._size) { return false; } else { return true; } }
bool operator==(const basic_string& str) { if (_size != str._size) { return false; } size_t end = 0; while (end < _size && end < str._size) { if (_str[end] != str._str[end]) { return false; } end++; } return true; }
bool operator!=(const basic_string& str) { return !(*this == str); }
bool operator>(const basic_string& str) { return !((*this < str) || (*this == str)); }
bool operator>=(const basic_string& str) { return !(*this < str); }
bool operator<=(const basic_string& str) { return !(*this > str); }
void Teststring() { string s("hello world"); // 3种遍历方式: // 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符, // 另外以下三种方式对于string而言,第一种使用最多 // 1. for+operator[] for(size_t i = 0; i < s.size(); ++i) cout<<s[i]<<endl; // 2.迭代器 string::iterator it = s.begin(); while(it != s.end()) { cout<<*it<<endl; ++it; } string::reverse_iterator rit = s.rbegin(); while(rit != s.rend()) { cout<<*rit<<endl; rit++; } // 3.范围for for(auto ch : s) cout << ch << endl; }
牛客网:
leetcode:
模拟实现stl是个无聊、耗时的过程,但是能够帮助我们很深刻地理解指针
、数据结构
、面向对象编程
以及逻辑思维能力
、代码能力
《 STL源码剖析》------侯捷,打算读一读
本博客所有代码:github、gitee