【Cpp】第五章-STL_string类

string类

STL

  STLStandard Template Library的简称,中文名为是标准模板库,在Cpp中模板是构成泛型编程的基础,我们利用模板可以极大程度地提高我们的代码复用率,但是如果模板要我们现写也有点过于繁琐,不过好在Cpp中为我们写代码方便为我们制作了一套标准地模板库,供我们直接使用十分方便。

STL的版本

  STL发展至今也不是一气呵成的,随着发展和进化,STL一共出现了四大版本。

HP版本

  这个版本是STL的原始版本,由Alexander Stepanov、Meng Lee在惠普实验室完成,是所有STL版本的始祖。并且此版本秉承开源精神,允许任何人免费运用,拷贝,商用,传播,修改这些代码,唯一的条件也只是要求需要像原始版本一样开源使用。

P.J.版本

  这个版本由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不可公开或修改,可读性较差。

RW版本

  这个版本由Rouge Wage公司开发,继承自HP版本,被C++ Builder采用,不能公开或修改,可读性一般。

SGI版本

  这个版本由Silicon Graphics Computer Systems,Inc公司开发,继承自HP版本,被GCC采用,可移植性较好,可公开,修改,贩卖,可读性很高。也是我们学习主要参考的版本。

STL六大组件

  STL中包含六大组件,他们共同组成STL互相协同工作。

容器

  string, vector, list, deque, map, set, multimap, multiset

配接器

  stack, queue, priority_queue

算法

  find, swap, reverse, sort, merge...

空间适配器

  allocator

迭代器

  iterator, const_iterator, reverse_iterator, const_reverse_iterator

仿函数

  greater, less...

  STL在日常编程中无论是笔试还是项目都十分常用,必须多用多练,并且自己实现一遍才能熟练掌握。STL(包扩Cpp绝大部分库)学习可分为三个层次:

  1、熟用STL
  2、了解泛型技术d的内涵与STL的学理乃至实作
  3、扩充STL
  总结就是能用,明理,能扩展。

string类

  string类时STL中专门用于字符串处理的容器。在C语言中我们利用字符数组或字符指针来构成字符串,所有字符串使用十分不方便,库中为字符串提供的接口也并不便于使用,于是在Cpp中有了string模板类,这个容器可以帮助我们更加方便的使用字符串,并且帮助我们封装了很多字符串相关的常用接口。

常用接口

构造函数

  string中提供了各种构造函数帮助我们构造字符串。

1
2
3
4
5
string(); //构造空的string类对象,即空字符串
string(const char *s);// 用C-string来构造string类对象
string(size_t n, char c);//string类对象中包含n个字符c
string(const string &s);//拷贝构造函数
string(const string &s, size_t n);//用s中的前n个字符构造新的string类对象 return 0;

容量相关接口

1
2
3
4
5
6
7
8
size_t size() const;			  // 返回字符串有效字符长度
size_t length() const; // 返回字符串有效字符长度
size_t capacity() const; // 返回空间总大小
bool empty() const; // 检测字符串释放为空串,是返回true,否则返回false
void clear(); //清空有效字符
void resize(size_t n, char c); // 将有效字符的个数该成n个,多出的空间用字符c填充
void resize(size_t n); // 将有效字符的个数改成n个,多出的空间用0填充
void reserve(size_t res_arg = 0); // 为字符串预留空间

访问相关接口

1
2
char& operator[] (size_t pos); 	  	    //返回pos位置的字符,非const string类对象调用
const char& operator[] (size_t pos); //const返回pos位置的字符,const string类对象调用

修改相关接口

1
2
3
4
5
6
7
8
9
10
void push_back(char c);							//在字符串后尾插字符c
string& append (const char* s); //在字符串后追加一个字符串
string& operator+=(const string& str); //在字符串后追加字符串str
string& operator+=(const char* s); //在字符串后追加字符串
string& operator+=(char c); //在字符串后追加字符c
const char* c_str()const; //返回C格式字符串
size_t find (char c, size_t pos = 0) const; //从字符串pos位置开始往后找字符c,返回该字符在字符串中的位置
size_t rfind(char c, size_t pos = npos); //从字符串pos位置开始往前找字符c,返回该字符在字符串中的位置
string substr(size_t pos = 0, size_t n= npos); //const在str中从pos位置开始,截取n个字符,然后将其返回
string& erase (size_t pos = 0, size_t len = npos); //从pos位置起删除串中npos个字符

迭代器相关

  迭代器十分类似于指针,我们可以将其等同于一个自定义类型的指针,用它我们可以完成容器内的遍历,增加,删除等操作,STL中容器的很多功能也为迭代器设计了很多接口,其中最为常用的还是取到一个容器的迭代器。

1
2
3
4
iterator begin();					//取到头部迭代器
const_iterator begin() const; //取到头部常迭代器
iterator end(); //取到尾部迭代器
const_iterator end() const; //取到尾部常迭代器

其他接口

1
2
3
4
5

string operator+ (const string& lhs, const string& rhs); //在lhs串后拼接rhs串
istream& operator>> (istream& is, string& str); //输入运算符重载
istream& getline (istream& is, string& str); //获取一行字符串
relational operators //大小比较

综合运用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>
#include <string>
using namespace std;
int main()
{
string str = "123456";//单参构造的隐式类型转换 + 拷贝构造
for(int i = 0; i < str.size(); i++)//size()取出长度
{
cout << str[i] << " ";//operator[]重载的运用
}
cout << endl;
str.append("abc");//append()接口
str.push_back('d');//push_back接口使用
str += "efg";//operator += 重载使用
//迭代器的应用
string::iterator it = str.begin();
while(it != str.end())
{
cout << * it << " ";
it++;
}
}


1 2 3 4 5 6
1 2 3 4 5 6 a b c d e f g

实现

  学习STL要熟用,明理,能扩展,那么第二部明理我们就要自己实现封装一个string类。根据库中string常用接口我们也实现其基本功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
#include <assert.h>
#include <string.h>
#include <cstdio>
#include <algorithm>
class String
{
friend std::ostream &operator<<(std::ostream &os, String str);
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;
}
static size_t npos;
//构造函数
String(const char* str = "")//要进行深拷贝
:_str(nullptr)
,_capacity(0)
{
_size = strlen(str);
//重新给容量Reserve()
Reserve(_size);
//strcpy()拷贝给成员变量
strcpy(_str, str);
}
//拷贝构造,要使用深拷贝
//所谓深拷贝就是创立独立的内存空间并将目标对象中的值拷贝过来
//而不是单纯的让指针等于目标拷贝对象中的指针
//注意:拷贝构造和operator=重载都是不拷贝容量大小的
//传统写法:创建新的独立内存,销毁原来的内存空间,更新_size, _capacity的值
//String(const String& str)
// :_str(nullptr)
// ,_size(0)
// ,_capacity(0)
//{
// Resize(str.Size());
// strcpy(_str, str._str);
//}
////operator=重载和拷贝构造类似,先用传统写法实现
//String& operator=(const String& str)
//{
// if(this != &str)
// {
// Resize(str.Size());
// strcpy(_str, str._str);
// }
//}
//现代写法,另外创建对象让其等于要拷贝的对象,交换两个对象即可
String(const String& str)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
String temp(str._str);
Swap(temp);
}
//现代写法,利用拷贝构造函数创建临时对象,交换两个对象,临时对象在函数结束时也会自动释放
String& operator=(String str)
{
Swap(str);
}
//交换两个字符串,浅拷贝,不另申请内存空间
void Swap(String& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
//析构函数
~String()
{
if(_str)
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
}
//返回_size
size_t Size() const
{
return _size;
}
//返回_capacity
size_t Capacity() const
{
return _capacity;
}
//在某个下标插入一个字符
void Insert(size_t pos, char ch)
{
assert(pos <= _size);
//容量满了,扩容
if(_size == _capacity)
{
Reserve(2 * _capacity);
}
for(int i = _size; i > pos; i--)
{
_str[i] = _str[i - 1];
}
_str[pos] = ch;
_size++;
_str[_size] = '\0';
}
//在某个下标处插入一个字符串
void Insert(size_t pos, const char* str)
{
assert(pos <= _size);
int len = strlen(str);
//容量不够扩容
if(_size + len > _capacity)
{
Reserve(len + _size);
}
for(int i = len + _size - 1; i > pos + len - 1; i--)
{
_str[i] = _str[i - len];
}
for(int i = pos; i < pos + len; i++)
{
_str[i] = str[i - pos];
}
_size += len;
}
//+=运算符重载
String& operator+=(char ch)
{
Push_back(ch);
}
String& operator+=(const char* str)
{
Append(str);
}
//删除pos下标开始的npos个字符
void Erase(size_t pos, size_t npos)
{
assert(pos < _size);
for(int i = pos; i < _size - npos; i++)
{
_str[i] = _str[i + npos];
}
_size -= npos;
_str[_size] = '\0';
}
//从pos开始找第一个字符为ch返回其下标
size_t Find(const char ch, size_t pos = 0)
{
assert(pos < _size);
for(size_t i = pos; i < _size; i++)
{
if(_str[i] == ch)
{
return i;
}
}
return npos;
}
//从pos开始找第一个子串为str返回其下标
size_t Find(const char* str, size_t pos = 0)
{
assert(pos < _size);
for(size_t i = pos; i < _size; i++)
{
if(_str[i] == str[0])
{
int j = i;
while (j < i + strlen(str) && _str[j] != '\0')
{
if (_str[j] != str[j - i])
{
break;
}
j++;
}
//子串遍历完毕,子串与要查找的串完全匹配
if (j == i + strlen(str))
{
return i;
}
//主串遇到结尾,长度不够不用继续查找了
else if(_str[j] == '\0')
{
break;
}
//其他情况本次子串与要查找的串匹配不上,继续下一次子串查找
}
}
return npos;
}
//在尾部插入字符
void Push_back(char ch)
{
Insert(_size, ch);
}
//字符串拼接
void Append(const char* str)
{
Insert(_size, str);
}
//重新给容量,并且要求容量永远为8的整数倍
void Reserve(size_t n)
{
if(n > _capacity || (n == 0 && _capacity == 0))
{
size_t newCapacity = n;
if(newCapacity % 8 != 0)
{
newCapacity = (((newCapacity / 8) + 1) * 8);
}
else
{
newCapacity += 8;
}
char* newStr = new char[newCapacity];
if(newStr && _str)
{
strcpy(newStr, _str);
}
//释放旧空间
delete[] _str;
_str = newStr;
_capacity = newCapacity - 1;
return;
}
}
void Resize(size_t size, char ch = '\0')
{
if(size < _size)
{
_size = size;
_str[_size] = '\0';
}
else
{
Reserve(size);
for(size_t i = _size; i < size; i++)
{
_str[i] = ch;
}
_size = size;
_str[_size] = '\0';
}
}
//>运算符重载
bool operator>(const String& str)
{
if(strcmp(_str, str._str) > 0)
{
return true;
}
return false;
}
bool operator==(const String& str)
{
if(strcmp(_str, str._str) == 0)
{
return true;
}
return false;
}
bool operator>=(const String& str)
{
if(*this > str || *this == str)
{
return true;
}
return false;
}
bool operator<(const String& str)
{
if(*this >= str)
{
return false;
}
return true;
}
bool operator<=(const String& str)
{
if(*this < str || *this == str)
{
return true;
}
return false;
}
bool operator!=(const String& str)
{
if(*this == str)
{
return false;
}
return true;
}
//+运算符重载
String operator+(char ch)
{
String temp(*this);
temp.Push_back(ch);
return temp;
}
String operator+(const char* str)
{
String temp(*this);
temp.Append(str);
return temp;
}
//取类中的字符串
char* c_str()
{
return _str;
}
//operator[]重载
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
//operator[]重载
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
private:
char* _str;
size_t _size;
size_t _capacity;
};
std::ostream& operator<<(std::ostream& os, String str)
{
os << str._str;
return os;
}
size_t String::npos = -1;

-------------本文结束感谢您的阅读!-------------
记录学习每一分,感谢您的赞助