番禺网站建设策划,十款看免费行情的软件,wordpress install,wordpress中文论坛插件1. string简介
C语言中#xff0c;可以用字符数组来存储字符串#xff0c;如#xff1a;
char ch[] hello world;
C中#xff0c;可以使用string类对象来存储字符串#xff0c;使用起来比C语言中的字符数组要方便得多#xff0c;而且不用考虑容量的问题。…1. string简介
C语言中可以用字符数组来存储字符串如
char ch[] hello world;
C中可以使用string类对象来存储字符串使用起来比C语言中的字符数组要方便得多而且不用考虑容量的问题。
本篇博客我会以实用为主介绍string类常用的接口包括常见的构造函数、容量操作、访问及遍历操作、修改操作、非成员函数等等。由于是实用为主某些不常用的接口我不做介绍读者在使用时请自行查询文档。接着通过这些知识做几道练习题通过题目了解这些接口实际的用法。最后我会讲解string的底层实现原理并模拟实现。
2. string类对象的构造
2.1 string();
无参的构造函数可以构造出空的string类对象即空字符串。
string s; // 等价于string s();
2.2 string(const char* s);
用C-string来构造string类对象。
string s(hello world);
cout s endl;
输出
hello world
2.3 string(size_t n, char c);
string类对象中包含n个字符c。
string s(5, a);
cout s endl;
输出
aaaaa
2.4 string(const string s);
拷贝构造函数使用一个string类对象构造另一个string类对象。
string s1(hello world);
string s2(s1);
cout s1 endl;
cout s2 endl;
输出
hello world
hello world
2.5 使用迭代器构造
templateclass InputIterator
string(InputIterator first, InputIterator last);
利用任意类型的迭代器进行构造。
string s1(hello world);
string s2(s1.begin(), --s1.end());
cout s2 endl; 输出
ello worl
3. string类对象的容量操作
3.1 size
size_t size() const;
返回字符串有效字符长度同length。引入size是为了与其他容器接口保持一致我习惯使用size而不是length。
string s(hello world);
cout s.size() endl;
输出
11
3.2 length
size_t length() const;
同size返回字符串有效字符长度。
string s(hello world);
cout s.length() endl;
输出
11
3.3 capacity
size_t capacity() const;
返回空间总大小至少是size。
string s(hello world);
cout s.capacity() endl;
visual studio 2022环境下输出
15
3.4 empty
bool empty() const;
检测字符串是否为空串是返回true否则返回false。
string s1;
cout s1.empty() endl;
输出
1
3.5 clear
void clear();
清空有效字符一般不改变底层空间大小。
string s(hello world);
s.clear();
cout s.empty() endl;
输出
1
3.6 reserve
void reserve(size_t n 0);
为字符串预留空间不改变有效元素个数。当n比底层空间大时会扩容扩容后空间至少为n当n比底层空间小时一般不改变底层空间。
string s;
s.reserve(100);
cout s.capacity() endl;
visual studio 2022环境下输出
111
3.7 resize
void resize(size_t n, char c \0);
将有效字符的个数改成n个。当n比字符串长度小时只保留前n个字符当n比字符串长度大时会在后面填充字符c直到字符串长度为n当n比字符串底层空间大时会扩容扩容后空间至少为n相当于执行了reserve(n)。
string s;
s.resize(5, x);
cout s endl;
s.resize(7, y);
cout s endl;
s.resize(3);
cout s endl;
输出
xxxxx
xxxxxyy
xxx
4. string类对象的访问及遍历操作
4.1 operator[]
char operator[](size_t pos);
const char operator[](size_t pos) const;
返回pos位置的字符。非const对象调用后返回的字符可读可写const对象调用后返回的字符只读不写。pos必须在有效的下标范围内。
string s1(hello world);
// 可读可写
s1[0] H; // Hello world
cout s1[0] endl; // Hconst string s2(hello world);
// 只读不写
// s2[0] H; // 错误用法
cout s2[0] endl; // h
输出
H
h
4.2 begin end
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
begin获取指向第一个字符的迭代器end获取指向最后一个字符的下一个位置的迭代器。
非const对象调用后返回的iterator指向的字符可读可写const对象调用后返回的const_iterator指向的字符只读不写。
string s1(hello world);
string::iterator it s1.begin();
// 可读可写
while (it ! s1.end())
{(*it);cout *it ;it;
}
cout endl;const string s2(hello world);
// 只读不写
string::const_iterator cit s2.begin();
while (cit ! s2.end())
{// (*cit); // 错误用法cout *cit ;cit;
}
cout endl;
输出
i f m m p ! x p s m e
h e l l o w o r l d
4.3 rbegin rend
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
同begin和end只不过是反向的。非const对象调用后返回的reverse_iterator指向的字符可读可写const对象调用后返回的const_reverse_iterator指向的字符只读不写。
string s1(hello world);
string::reverse_iterator rit s1.rbegin();
// 可读可写
while (rit ! s1.rend())
{(*rit);cout *rit ;rit;
}
cout endl;const string s2(hello world);
// 只读不写
string::const_reverse_iterator crit s2.rbegin();
while (crit ! s2.rend())
{// (*crit); // 错误用法cout *crit ;crit;
}
cout endl;
输出
e m s p x ! p m m f i
d l r o w o l l e h
4.4 范围for
遍历string类对象更简洁的形式底层会转换为迭代器。
string s(hello world);
for (auto ch : s)
{cout ch ;
}
cout endl;
输出
h e l l o w o r l d
5. string类对象的修改操作
5.1 push_back
void push_back(char c);
在字符串后尾插字符c。
string s(hello world);
s.push_back(!);
cout s endl;
输出
hello world!
5.2 append
string append(const string str);
string append(const char* s);
string append(size_t n, char c);
在字符串后追加一个字符串。
string s1(abc);
string s2(defg);
s1.append(s2);
cout s1 endl;
s1.append(hijkl);
cout s1 endl;
s1.append(2, m);
cout s1 endl;
输出
abcdefg
abcdefghijkl
abcdefghijklmm
5.3 operator
string operator(const string str);
string operator(const char* s);
string operator(char c);
在字符串后追加一个字符串或者字符。
string s1(abc);
string s2(defg);
s1 s2;
cout s1 endl;
s1 hijkl;
cout s1 endl;
s1 m;
cout s1 endl;
输出
abcdefg
abcdefghijkl
abcdefghijklm
5.4 c_str
const char* c_str() const;
返回一个C格式字符串以字符\0结尾。
string s(abcde);
cout s.c_str() endl;
输出
abcde
5.5 find
size_t find(const string str, size_t pos 0) const;
size_t find(const char* s, size_t pos 0) const;
size_t find(char c, size_t pos 0) const;
static const size_t npos -1;
从pos位置开始往后找字符串或字符返回该字符串或字符第一次出现的位置。若找不到返回npos。
string s1(hello world);
size_t ret s1.find(w);
if (ret ! string::npos)cout s1[ret] endl;ret s1.find(wor);
if (ret ! string::npos)cout s1[ret] endl;string s2(or);
ret s1.find(s2);
if (ret ! string::npos)cout s1[ret] endl;
输出
w
w
o
5.6 rfind
size_t rfind(const string str, size_t pos npos) const;
size_t rfind(const char* s, size_t pos npos) const;
size_t rfind(char c, size_t pos npos) const;
static const size_t npos -1;
类似find从pos位置开始往前找字符串或字符。相当于返回该字符串或字符最后一次出现的位置。若找不到返回npos。
5.7 substr
string substr(size_t pos 0, size_t len npos) const;
从pos位置开始向后截取len个字符并返回。若len太大截取时超出字符串范围则截取到字符串的尾部。
string s(hello world);
string ret s.substr(6, 3);
cout ret endl;
输出
wor
6. string类非成员函数
6.1 operator
string operator(const string str1, const string str2);
string operator(const char* s, const string str);
string operator(const string str, const char* s);
string operator(char c, const string str);
string operator(const string str, char c);
拼接字符串和字符串或者字符。尽可能少用因为传值返回导致深拷贝效率低。
string s1(hello );
string s2(world);
cout s1 s2 endl;
cout s1 world endl;
cout hello s2 endl;
cout s1 a endl;
cout b s2 endl;
输出
hello world
hello world
hello world
hello a
bworld
6.2 operator
istream operator(istream in, string str);
流插入运算符重载以空格、换行作为分隔符。
string s(hello world);
cin s;
cout s endl;
输出示例第一行为输入
abc def
abc
6.3 operator
ostream operator(ostream out, const string str);
流提取运算符重载。
string s(hello world);
cout s endl;
输出
hello world
6.4 getline
istream getline(istream in, string str, char delim \n);
获取字符并存储到字符串中直到遇到字符delim返回该字符串。该字符串不包括delim。一般用来获取一行字符串。
string s;
getline(cin, s);
cout s endl;
输出示例第一行为输入
abc def
abc def
6.5 关系运算符
bool operator(const string str1, const string str2);
bool operator(const string str, const char* s);
bool operator(const char* s, const string str);
bool operator(const string str1, const string str2);
bool operator(const string str, const char* s);
bool operator(const char* s, const string str);
bool operator(const string str1, const string str2);
bool operator(const string str, const char* s);
bool operator(const char* s, const string str);
bool operator(const string str1, const string str2);
bool operator(const string str, const char* s);
bool operator(const char* s, const string str);
bool operator(const string str1, const string str2);
bool operator(const string str, const char* s);
bool operator(const char* s, const string str);
bool operator!(const string str1, const string str2);
bool operator!(const string str, const char* s);
bool operator!(const char* s, const string str);
比较字符串的大小比较的是字符串对应位置的字符的ASCII码。
7. 练习
7.1 仅仅反转字母
仅仅反转字母https://leetcode.cn/problems/reverse-only-letters/description/
定义左指针left和右指针right两个指针从两边向中间移动直到相遇为止。若遇到字母就交换左右指针指向的字符。
class Solution {
public:string reverseOnlyLetters(string s) {int left 0, right s.size() - 1;while (left right){// 左边找字母while (left right !isalpha(s[left]))left;// 右边找字母while (left right !isalpha(s[right]))--right;swap(s[left], s[right--]);}return s;}
};
7.2 找字符串中第一个只出现一次的字符
找字符串中第一个只出现一次的字符原题链接https://leetcode.cn/problems/first-unique-character-in-a-string/description/
字符串中只会出现小写字母所以可以用计数排序的思路定义数组countA记录每个字符出现的次数把每个字符ch映射到下标为ch-a的位置。第一次遍历字符串统计出每个字符出现的次数。第二次遍历字符串查找只出现一次的字符。
class Solution {
public:int firstUniqChar(string s) {int countA[26] {0};for (auto ch : s)countA[ch - a];for (int i 0; i s.size(); i)if (countA[s[i] - a] 1)return i;return -1;}
};
7.3 字符串里面最后一个单词的长度
字符串里面最后一个单词的长度原题链接https://www.nowcoder.com/practice/8c949ea5f36f422594b306a2300315da?tpId37tqId21224rp5ru/activity/ojqru/ta/huawei/question-ranking
由于输入包含空格所以要使用getline获取字符串。接着使用rfind查找最后一个空格从而确定最后一个单词的长度。
#include iostream
using namespace std;int main() {string s;getline(cin, s);// 查找最后一个空格size_t ret s.rfind( );if (ret ! string::npos)cout s.size() - ret - 1 endl;else // 字符串没有空格cout s.size() endl;return 0;
}
7.4 验证一个字符串是否是回文
验证一个字符串是否是回文原题链接https://leetcode.cn/problems/valid-palindrome/description/
定义左指针left和右指针right两个指针从两边向中间移动直到相遇为止。若遇到数字或字母判断它们指向的字符转小写字母后是否相同从而判断是否是回文串。
class Solution {
public:bool isPalindrome(string s) {int left 0, right s.size() - 1;while (left right){// 左边找数字字母while (left right !isalnum(s[left]))left;// 右边找数字字母while (left right !isalnum(s[right]))--right;if (tolower(s[left]) ! tolower(s[right--]))return false;}return true;}
};
7.5 字符串相加
字符串相加原题链接https://leetcode.cn/problems/add-strings/description/
从最低位开始每次取对应位的数字相加并计算进位。考虑把相加的结果插入到字符串头部但是反复的头插效率太低所以应把相加的结果尾插到字符串中再整体反转。注意到插入的过程中可能会多次扩容所以先用reserve保留足够的空间避免扩容的消耗。
class Solution {
public:string addStrings(string num1, string num2) {string ret;ret.reserve(max(num1.size(), num2.size()) 1);int carry 0; // 进位int end1 num1.size() - 1, end2 num2.size() - 1;while (end1 0 || end2 0){// 取出对应位置的数字int n1 end1 0 ? num1[end1] - 0 : 0;int n2 end2 0 ? num2[end2] - 0 : 0;int sum n1 n2 carry;carry sum / 10;ret ((sum % 10) 0);--end1;--end2;}if (carry 1)ret 1;reverse(ret.begin(), ret.end());return ret;}
};
8. 最简易模拟实现
如果不考虑容量问题理论上只需要一个char*类型的指针就能管理字符串了。要实现一个最简易的string只需要实现利用C-string的构造函数、拷贝构造、赋值运算符重载以及析构函数。除此之外可以顺手实现C11中的移动构造和移动赋值以及size、c_str、关系运算符、operator[]等接口。
8.1 C-string构造
利用C-string构造只需要new出足够大的空间多开一个空间存储\0。
string(const char* s ):_str(new char[strlen(s)1])
{strcpy(_str, s);
}
8.2 拷贝构造
拷贝构造可以利用C标准规定的现代写法转为利用C-string构造。
string(const string str):string(str._str)
{}
8.3 赋值运算符重载
赋值运算符重载仍然使用现代写法采取传值传参。
string operator(string tmp)
{swap(tmp);return *this;
}
其中swap交换对应的_str指针即可。
void swap(string str)
{std::swap(str._str, _str);
}
8.4 析构函数
析构函数直接delete即可。
~string()
{delete[] _str;_str nullptr;
}
8.5 移动构造和移动赋值
C11中的移动构造和移动赋值只需要交换_str指针。
string(string str) noexcept:_str(str._str)
{str._str nullptr;
}string operator(string tmp) noexcept
{swap(tmp);return *this;
}
8.6 c_str size operator[]
接下来c_str、size、operator[]都可以顺手实现。
size_t size() const
{return strlen(_str);
}const char* c_str() const
{return _str;
}char operator[](size_t pos)
{assert(pos size());return _str[pos];
}const char operator[](size_t pos) const
{assert(pos size());return _str[pos];
}
8.7 关系运算符
关系运算符重载作为非成员函数只需实现和其余复用即可。
bool operator(const string str1, const string str2)
{return strcmp(str1.c_str(), str2.c_str()) 0;
}bool operator(const string str1, const string str2)
{return strcmp(str1.c_str(), str2.c_str()) 0;
}bool operator(const string str1, const string str2)
{return str1 str2 || str1 str2;
}bool operator(const string str1, const string str2)
{return !(str1 str2);
}bool operator!(const string str1, const string str2)
{return !(str1 str2);
}bool operator(const string str1, const string str2)
{return !(str1 str2);
}
8.8 完整实现
完整的实现及测试代码如下
#includeiostream
#includestring.h
#includeassert.h
#includeutility
using namespace std;namespace xbl
{class string{public:string(const char* s ):_str(new char[strlen(s)1]){strcpy(_str, s);}string(const string str):string(str._str){}string(string str) noexcept:_str(str._str){str._str nullptr;}string operator(string tmp){swap(tmp);return *this;}string operator(string tmp) noexcept{swap(tmp);return *this;}~string(){delete[] _str;_str nullptr;}size_t size() const{return strlen(_str);}const char* c_str() const{return _str;}char operator[](size_t pos){assert(pos size());return _str[pos];}const char operator[](size_t pos) const{assert(pos size());return _str[pos];}void swap(string str){std::swap(str._str, _str);}private:char* _str;};bool operator(const string str1, const string str2){return strcmp(str1.c_str(), str2.c_str()) 0;}bool operator(const string str1, const string str2){return strcmp(str1.c_str(), str2.c_str()) 0;}bool operator(const string str1, const string str2){return str1 str2 || str1 str2;}bool operator(const string str1, const string str2){return !(str1 str2);}bool operator!(const string str1, const string str2){return !(str1 str2);}bool operator(const string str1, const string str2){return !(str1 str2);}void test_string(){string s1;cout s1.c_str() endl;string s2(hello world);cout s2.size() endl;cout s2[1] endl;string s3(s2);string s4 s3;s1 s2;cout s1.c_str() endl;cout s2.c_str() endl;cout s3.c_str() endl;cout s4.c_str() endl;cout (s1 s2) endl;}
}
输出 11
e
hello world
hello world
hello world
hello world
1
9. 带size和capacity的版本
最简易模拟实现的思路适用于面试时被要求模拟实现string。特点是实现简单但效率不高因为strlen遍历字符串的时间复杂度是O(N)。一个功能完善的string可以考虑带上size_t类型的_size和_capacity成员变量分别用来记录有效元素的个数和容量。这个版本也是我重点要讲解的版本。
9.1 C-string构造
这样C-string的构造函数就可以这样写。
string(const char* s ):_size(strlen(s))
{_capacity _size 0 ? 3 : _size;_str new char[_capacity 1];strcpy(_str, s);
}
观察监视窗口 9.2 拷贝构造
拷贝构造不能简单地利用C-string构造实现了这是因为下面的情况中C-string以\0结尾那么\0后面的字符就不会被识别为有效字符。 所以正确的做法是使用memcpy把空间上的有效数据拷贝过去。
string(const string str):_str(new char[str._size 1]), _size(str._size), _capacity(str._size)
{memcpy(_str, str._str, (_size 1) * sizeof(char));
}
观察监视窗口 9.3 赋值运算符重载
采用现代写法利用传值传参的深拷贝。
string operator(string tmp)
{swap(tmp);return *this;
}void swap(string str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
} 9.4 析构函数
释放_str指针指向的空间。
~string()
{delete[] _str;_size _capacity 0;
}
9.5 移动构造和移动赋值
交换对应的数据即可。
string(string str) noexcept:_str(nullptr)
{swap(str);
}string operator(string str) noexcept
{swap(str);return *this;
}
9.6 c_str size capacity
返回对应的成员变量。
const char* c_str() const
{return _str;
}size_t size() const
{return _size;
}size_t capacity() const
{return _capacity;
}
9.7 operator[]
实现const和非const两个版本。
char operator[](size_t pos)
{assert(pos _size);return _str[pos];
}const char operator[](size_t pos) const
{assert(pos _size);return _str[pos];
}
9.8 关系运算符
不能直接使用strcmp因为下面的情况中如果使用C-string比较s1和s2应该相等但事实上s1s2原因是s2更长。
std::string s1(hello world);
std::string s2(hello world);
s2 \0;cout (s1 s2) endl;
cout (s1 s2) endl;
输出
0
1
所以要手动比较。先考虑operator同时遍历两个字符串若遇到不相等的字符则比较结束结果是“小于”或者“大于”判断是否是“小于”即可。若其中一个字符串遍历完了那么长的字符串更大。再考虑operator当两个字符串的size相同且所有字符均相同则字符串相等。其余的关系运算符复用operator和operator即可。
bool operator(const string str1, const string str2)
{size_t i 0;while (i str1.size() i str2.size()){if (str1[i] ! str2[i]){return str1[i] str2[i];}i;}// 若有效字符均相等则长的字符串更大return str1.size() str2.size();
}bool operator(const string str1, const string str2)
{if (str1.size() ! str2.size()){return false;}// size相同for (size_t i 0; i str1.size(); i){if (str1[i] ! str2[i]){return false;}}return true;
}bool operator(const string str1, const string str2)
{return str1 str2 || str1 str2;
}bool operator(const string str1, const string str2)
{return !(str1 str2);
}bool operator(const string str1, const string str2)
{return !(str1 str2);
}bool operator!(const string str1, const string str2)
{return !(str1 str2);
}
9.9 clear
直接把_size改为0。
void clear()
{_str[0] \0;_size 0;
}
9.10 reserve
如果reserve的参数n_capacity需要扩容如果n_capacity无需处理。
void reserve(size_t n)
{if (n _capacity){// 扩容拷贝数据char* tmp new char[n 1];memcpy(tmp, _str, _size 1);_str tmp;_capacity n;}
}
9.11 insert
首先检查容量空间不够了就用reserve扩容这里实现为至少扩容2倍。接着使用memmove挪动数据空出足够的空间注意计算挪动数据的个数要挪动的数据是[pos,_size]包括字符串末尾的\0总计_size-pos1字节。最后插入数据可以使用memset或者strncpy。
string insert(size_t pos, size_t n, char c)
{assert(pos _size);if (_size n _capacity){// 至少扩2倍reserve(max(_size n, _capacity * 2));}// 挪动数据memmove(_str pos n, _str pos, (_size - pos 1) * sizeof(char));// 插入memset(_str pos, c, n * sizeof(char));_size n;return *this;
}string insert(size_t pos, const char* s)
{assert(pos _size);size_t len strlen(s);if (_size len _capacity){// 至少扩2倍reserve(max(_size len, _capacity * 2));}// 挪动数据memmove(_str pos len, _str pos, (_size - pos 1) * sizeof(char));// 插入strncpy(_str pos, s, len);_size len;return *this;
}
在调用insert前后观察调试窗口 9.12 push_back append operator
复用insert即可。
void push_back(char c)
{insert(_size, 1, c);
}void append(const char* s)
{insert(_size, s);
}string operator(char c)
{push_back(c);return *this;
}string operator(const char* s)
{append(s);return *this;
}
在调用operator前后观察调试窗口 9.13 empty
判断_size是否为0即可。
bool empty() const
{return _size 0;
}
9.14 resize
若resize的参数n_size先考虑容量是否充足接着在字符串末尾插入n-_size个字符c。最后在字符串末尾设置\0。
void resize(size_t n, char c \0)
{if (n _size){if (n _capacity){reserve(max(n, _capacity * 2));}// 填充n-size个cmemset(_str _size, c, (n - _size) * sizeof(char));}_str[n] \0;_size n;
}
分n_sizen_size和n_size三种情况观察调试窗口 9.15 迭代器
string的迭代器用字符指针即可。begin指向起始位置_strend指向结束位置_str_size即\0的位置。
typedef char* iterator;
typedef const char* const const_iterator;iterator begin()
{return _str;
}const_iterator begin() const
{return _str;
}iterator end()
{return _str _size;
}const_iterator end() const
{return _str _size;
}
9.16 erase
挪动数据覆盖删除即可。注意计算挪动数据的个数要挪动的数据是(pos,_size]不包括pos位置包括末尾的\0总共_size-pos字节。
string erase(size_t pos 0)
{assert(pos _size);// 挪动数据覆盖删除memmove(_str pos, _str pos 1, (_size - pos) * sizeof(char));--_size;return *this;
}
观察调试窗口 9.17 find
不能使用strchr和strstr因为string内存储的字符串可能包含\0而C语言的函数遇到\0就不会继续查找了。
只能根据strchr和strstr的原理手动实现。对于字符的查找从pos位置开始匹配即可。对于字符串的查找也是从pos位置开始匹配对于每一个pos令下标i从pos开始j从0开始分别遍历_str[i]和s[j]直到越界或者出现不匹配的字符。
size_t find(char c, size_t pos 0) const
{if (pos _size){return npos;}for (size_t i pos; i _size; i){if (_str[i] c){return i;}}return npos;
}size_t find(const char* s, size_t pos 0) const
{if (pos _size){return npos;}size_t i 0; // _strsize_t j 0; // swhile (pos _size){i pos;j 0;while (i _size s[j] _str[i] s[j]){i;j;}if (s[j] \0){// 匹配成功return pos;}pos;}// 匹配失败return npos;
}
9.18 operator
使用范围for取出所有字符并插入到流中即可。
ostream operator(ostream out, const string str)
{for (auto ch : str){out ch;}return out;
}
9.19 operator
反复使用istream类对象的get获取字符并尾插到str中即可。
istream operator(istream in, string str)
{str.clear();char ch \0;in.get(ch);while (ch ! ch ! \n){str.push_back(ch);in.get(ch);}return in;
}
但是上面的写法会导致频繁的扩容。建议定义一个字符数组buff每次读取字符后先放到buff中buff放满了再存储到str中。
istream operator(istream in, string str)
{str.clear();char buff[128] { 0 };char ch \0;in.get(ch);size_t i 0;while (ch ! ch ! \n){buff[i] ch;// buff放满了再添加到str中if (i 127){buff[i] \0;str.append(buff);i 0;}in.get(ch);}buff[i] \0;str buff;return in;
}
9.20 完整实现
至此string类的常用接口都实现完了下面附上完整的实现及测试代码供读者调试。
#includeiostream
#includestring
#includealgorithm
#includeassert.h
using namespace std;namespace xbl
{class string{public:typedef char* iterator;typedef const char* const const_iterator;public:iterator begin(){return _str;}const_iterator begin() const{return _str;}iterator end(){return _str _size;}const_iterator end() const{return _str _size;}string(const char* s ):_size(strlen(s)){_capacity _size 0 ? 3 : _size;_str new char[_capacity 1];strcpy(_str, s);}string(const string str):_str(new char[str._size1]), _size(str._size), _capacity(str._size){memcpy(_str, str._str, (_size 1) * sizeof(char));}string operator(string tmp){swap(tmp);return *this;}~string(){delete[] _str;_size _capacity 0;}string(string str) noexcept:_str(nullptr){swap(str);}string operator(string str) noexcept{swap(str);return *this;}void clear(){_str[0] \0;_size 0;}void reserve(size_t n){if (n _capacity){// 扩容拷贝数据char* tmp new char[n 1];memcpy(tmp, _str, _size 1);_str tmp;_capacity n;}}void resize(size_t n, char c \0){if (n _size){if (n _capacity){reserve(max(n, _capacity * 2));}// 填充n-size个cmemset(_str _size, c, (n - _size) * sizeof(char));}_str[n] \0;_size n;}void push_back(char c){insert(_size, 1, c);}void append(const char* s){insert(_size, s);}string operator(char c){push_back(c);return *this;}string operator(const char* s){append(s);return *this;}string insert(size_t pos, size_t n, char c){assert(pos _size);if (_size n _capacity){// 至少扩2倍reserve(max(_size n, _capacity * 2));}// 挪动数据memmove(_str pos n, _str pos, (_size - pos 1) * sizeof(char));// 插入memset(_str pos, c, n * sizeof(char));_size n;return *this;}string insert(size_t pos, const char* s){assert(pos _size);size_t len strlen(s);if (_size len _capacity){// 至少扩2倍reserve(max(_size len, _capacity * 2));}// 挪动数据memmove(_str pos len, _str pos, (_size - pos 1) * sizeof(char));// 插入strncpy(_str pos, s, len);_size len;return *this;}string erase(size_t pos 0){assert(pos _size);// 挪动数据覆盖删除memmove(_str pos, _str pos 1, (_size - pos) * sizeof(char));--_size;return *this;}void swap(string str){std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);}const char* c_str() const{return _str;}size_t size() const{return _size;}size_t capacity() const{return _capacity;}char operator[](size_t pos){assert(pos _size);return _str[pos];}const char operator[](size_t pos) const{assert(pos _size);return _str[pos];}bool empty() const{return _size 0;}size_t find(char c, size_t pos 0) const{if (pos _size){return npos;}for (size_t i pos; i _size; i){if (_str[i] c){return i;}}return npos;}size_t find(const char* s, size_t pos 0) const{if (pos _size){return npos;}size_t i 0; // _strsize_t j 0; // swhile (pos _size){i pos;j 0;while (i _size s[j] _str[i] s[j]){i;j;}if (s[j] \0){// 匹配成功return pos;}pos;}// 匹配失败return npos;}public:static const size_t npos;private:char* _str;size_t _size;size_t _capacity;};const size_t string::npos -1;bool operator(const string str1, const string str2){size_t i 0;while (i str1.size() i str2.size()){if (str1[i] ! str2[i]){return str1[i] str2[i];}i;}// 若有效字符均相等则长的字符串更大return str1.size() str2.size();}bool operator(const string str1, const string str2){if (str1.size() ! str2.size()){return false;}// size相同for (size_t i 0; i str1.size(); i){if (str1[i] ! str2[i]){return false;}}return true;}bool operator(const string str1, const string str2){return str1 str2 || str1 str2;}bool operator(const string str1, const string str2){return !(str1 str2);}bool operator(const string str1, const string str2){return !(str1 str2);}bool operator!(const string str1, const string str2){return !(str1 str2);}ostream operator(ostream out, const string str){for (auto ch : str){out ch;}return out;}istream operator(istream in, string str){str.clear();char buff[128] { 0 };char ch \0;in.get(ch);size_t i 0;while (ch ! ch ! \n){buff[i] ch;// buff放满了再添加到str中if (i 127){buff[i] \0;str.append(buff);i 0;}in.get(ch);}buff[i] \0;str buff;return in;}void test_string(){string s1(hello world);cout s1.c_str() endl;string s2(s1);cout s2.c_str() endl;string s4 s1;cout s4.c_str() endl;string s3(test);s3 s1;cout s3.c_str() endl;for (size_t i 0; i s3.size(); i){cout s3[i] ;}cout endl;string::iterator it s3.begin();while (it ! s3.end()){cout *it ;it;}cout endl;for (auto ch : s3){cout ch ;}cout endl;cout (s1 s3) endl;cout (s1 hello) endl;cout (s1 z) endl;s3.insert(5, 3, x);cout s3.c_str() endl;s3.insert(6, test);cout s3.c_str() endl;s2 !;cout s2.c_str() endl;s2 test;cout s2.c_str() endl;// nsizes1.resize(11);// nsizes1.resize(5);// nsizes1.resize(8, x);s4.erase(5);cout s4.c_str() endl;string s5(abcabbcabbbcabbbbc);size_t ret s5.find(c);if (ret ! string::npos){cout ret endl;}else{cout 没找到 endl;}ret s5.find(bbbc);if (ret ! string::npos){cout ret endl;}else{cout 没找到 endl;}cin s4 s5;cout s4 endl;cout s5 endl;}
}