C++ 进阶基础之六

大纲

string 容器

string 容器的概念

string 是 STL 的字符串类型,通常用来表示字符串。而在使用 string 之前,字符串通常是用 char* 表示的。stringchar* 都可以用来表示字符串,两者的区别如下:

  • string 是一个类,char* 是一个指向字符的指针
  • string 封装了 char* 来管理字符串,本质是一个 char* 类型的容器
  • string 不用考虑内存释放和越界的问题
  • string 负责管理 char* 所分配的内存。每一次 string 的复制,取值都由 string 类负责维护,不用担心复制越界和取值越界等问题
  • string 提供了一系列的字符串操作函数,例如:查找(find)、拷贝(copy)、删除(erase)、替换(replace)、插入(insert)

stirng 容器的 API

string 的构造函数

  • 默认构造函数:string();
  • 带参数的构造函数:
    • string(const char *s);,用字符串 s 初始化
    • string(int n, char c);,用 n 个字符 c 初始化
  • 拷贝构造函数:string(const string &str);

string 的长度

  • size_t size() const,返回当前字符串的长度,这里的长度不包括字符串的结尾的 \0 字符
  • size_t length() const;,返回当前字符串的长度,这里的长度不包括字符串的结尾的 \0 字符
  • bool empty() const;,判断当前字符串是否为空

值得一提的是,sizeof() 返回的是对象所占用空间的字节数,strlen() 返回的是字符数组中第一个 \0 前的字节数,string 的成员函数 size()length() 没有任何区别。

string 的赋值

  • string &operator=(const string &s);,把字符串 s 赋给当前的字符串
  • string &assign(const char *s);,把字符串 s 赋给当前的字符串
  • string &assign(const char *s, int n);,把字符串 s 的前 n 个字符赋给当前的字符串
  • string &assign(const string &s);,把字符串 s 赋给当前字符串
  • string &assign(int n, char c);,用 n 个字符 c 赋值给当前字符串
  • string &assign(const string &s, int start, int n);,把字符串 s 中从 start 开始的 n 个字符赋值给当前字符串

string 的子串

  • string substr(int pos=0, int n=npos) const;,返回由 pos 位置开始的 n 个字符组成的子字符串

string 的查找

  • int find(char c, int pos=0) const;,从 pos 位置开始查找字符 c 在当前字符串第一次出现的位置
  • int find(const char *s, int pos=0) const;,从 pos 位置开始查找字符串 s 在当前字符串第一次出现的位置
  • int find(const string &s, int pos=0) const;,从 pos 位置开始查找字符串 s 在当前字符串第一次出现的位置
  • int rfind(char c, int pos=npos) const;,从 pos 位置开始查找字符 c 在当前字符串中最后一次出现的位置
  • int rfind(const char *s, int pos=npos) const;,从 pos 位置开始查找字符串 s 在当前字符串中最后一次出现的位置
  • int rfind(const string &s, int pos=npos) const;,从 pos 位置开始查找字符串 s 在当前字符串中最后一次出现的位置

值得一提的是,当 find()rfind() 函数查找不到时,都会返回 -1;两者不同的是 find() 是正向查找,而 rfind() 是逆向查找,但是最终两个函数返回的位置均是字符 / 字符串出现的正向位置;若有重复字符 / 字符串时,则 rfind() 返回的是逆向查找到的字符 / 字符串在正向的位置(即最后一次出现的正向位置)。

string 的替换

  • string &replace(int pos, int n, const char *s);,删除从 pos 位置开始的 n 个字符,然后在 pos 位置插入字符串 s
  • string &replace(int pos, int n, const string &s);,删除从 pos 位置开始的 n 个字符,然后在 pos 位置插入字符串 s
  • void swap(string &s2);,交换当前字符串与字符串 s2 的值

string 的比较

  • int compare(const string &s) const;,与字符串 s 比较
  • int compare(const char *s) const;,与字符串 s 比较

compare() 函数的结果在 > 时返回 1,< 时返回 -1,= 时返回 0。字符串比较区分大小写,比较时参考字典顺序,排越前面的越小。大写的 A(65) 比小写的 a(97) 小。

string 的字符存储

  • char &at(int n);
  • char &operator[] (int n);
  • operator[]at() 均返回当前字符串中的第 n 个字符,但二者是有区别的
    • at() 在越界时会抛出异常,[] 在刚好越界时会返回 (char)0,再继续越界时,程序异常终止
    • 如果程序希望可以通过 try catch 捕获异常,则建议采用 at()

string 的区间插入

  • string &insert(int pos, const char *s);,在 pos 位置插入字符串 s,返回修改后的字符串
  • string &insert(int pos, const string &s);,在 pos 位置插入字符串 s,返回修改后的字符串
  • string &insert(int pos, int n, char c);,在 pos 位置插入 n 个字符 c,返回修改后的字符串

string 的区间删除

  • string &erase(int pos=0, int n=npos);,删除从 pos 位置开始的 n 个字符,返回修改后的字符串

string 的字符串拼接

  • string &operator+=(const string &s);,把字符串 s 连接到当前字符串的结尾
  • string &operator+=(const char *s);,把字符串 s 连接到当前字符串的结尾
  • string &append(const char *s); ,把字符串 s 连接到当前字符串的结尾
  • string &append(const char *s, int n);,把字符串 s 的前 n 个字符连接到当前字符串的结尾
  • string &append(const string &s); ,把字符串 s 连接到当前字符串的结尾
  • string &append(const string &s, int pos, int n);,把字符串 s 中从 pos 位置开始的 n 个字符连接到当前字符串的结尾
  • string &append(int n, char c); ,在当前字符串的结尾添加 n 个字符 c

从 string 取得 char*

  • const char *c_str() const;,返回一个以 \0 结尾的字符串的首地址

值得一提的是,char * 可以隐式转换为 string 类型,反过来则不可以,例如右边这种写法是合法的: char *p = "abc"; string str = p;

将 string 拷贝到 char* 指向的内存空间

  • int copy(char *s, int n, int pos=0) const;

将当前串中以 pos 位置开始的 n 个字符拷贝到以 s 为起始位置的字符数组中,返回实际拷贝的字符数量。特别注意,要保证指针 s 所指向的内存空间足以容纳当前的字符串,不然可能会发生越界。

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
#include <iostream>

using namespace std;

int main() {
// 默认构造函数
string str1;

// 拷贝构造函数
string str2 = str1;

// 有参构造函数
string str3("abced");
string str4(5, 'f');

// 基本赋值
str1 = "123456";
str2 = str3;
str3.assign("mnopq", 3);
str4.assign("45678", 1, 3); // 从0开始索引,1表示第2个字符
cout << "str1 = " << str1 << endl;
cout << "str2 = " << str2 << endl;
cout << "str3 = " << str3 << endl;
cout << "str4 = " << str4 << endl;

return 0;
}

程序运行输出的结果如下:

1
2
3
4
str1 = 123456
str2 = abced
str3 = mno
str4 = 567
  • string 容器的 API 调用
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
#include <iostream>

using namespace std;

int main() {
// 存储字符
string str1 = "abcde";
for (int i = 0; i < str1.size(); i++) {
// 第一种方式
cout << str1[i] << " ";
// 第二种方式
// cout << str1.at(i) << " ";
}
cout << endl;

// 字符串拼接
string str2 = "hello ";
string str3 = "world ";
str2 += str3;
str3.append("where");
cout << "str2 = " << str2 << endl;
cout << "str3 = " << str3 << endl;

// 字符串查找
string str4 = "My name is Peter";
int index1 = str4.find("name");
cout << "index1 = " << index1 << endl;
int index2 = str4.rfind("e");
cout << "index2 = " << index2 << endl;

// 字符串替换
string str5 = "abc123";
str5.replace(3, 3, "def");
cout << "str5 = " << str5 << endl;
string str6 = "123456";
string str7 = "654321";
str6.swap(str7);
cout << "str6 = " << str6 << endl;

// 字符串比较
string str8 = "ABC";
string str9 = "abc";
int result = str8.compare(str9); // 返回值小于等于-1
cout << "result = " << result << endl;

// 截取子字符串
string str10 = "124abc";
string str11 = str10.substr(1, 3);
cout << "str11 = " << str11 << endl;

// 字符串的区间插入
string str12 = "abcdef";
str12.insert(2, "123");
cout << "str12 = " << str12 << endl;

// 字符串的区间删除
string str13 = "123456";
str13.erase(2, 2);
cout << "str13 = " << str13 << endl;

// 从字符串取得 char *
string str14 = "hijkl";
const char *p1 = str14.c_str();
cout << "p1 = " << p1 << endl;

// char * 隐式类型转换为 string
char *p2 = "abc123";
string str15 = p2;
cout << "str15 = " << str15 << endl;

// 将 string 拷贝到 char* 指向的内存空间
char *p3 = new char[3];
string str16 = "hello jim";
int number = str16.copy(p3, 3, 2);
cout << "number = " << number << endl;
cout << "p3 = " << p3 << endl;
delete[] p3;

return 0;
}

程序运行输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a b c d e 
str2 = hello world
str3 = world where
index1 = 3
index2 = 14
str5 = abcdef
str6 = 654321
result = -32
str11 = 24a
str12 = ab123cdef
str13 = 1256
p1 = hijkl
str15 = abc123
number = 3
p3 = llo

map 容器

map 容器的概念

map 是 STL 的一个关联式容器,它提供一对一(其中第一个称为关键字,每个关键字只能在 map 中出现一次,第二个称为该关键字的值)的数据处理能力。map 容器存储的都是 pair 对象,也就是用 pair 类模板创建的键值对。其中,各个键值对的键和值可以是任意数据类型,包括 C++ 基本数据类型(int、double 等)、使用结构体或类自定义的类型。通常情况下,map 容器中存储的各个键值对都选用 string 字符串作为键的类型。map 内部自建了一颗红黑树 (一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在 map 内部所有的数据都是有序的。map 的特点是增加和删除节点对迭代器的影响很小,除了那个被操作的节点,对其他的节点都没有什么影响。值得一提的是,使用 map 容器存储的各个键值对,键的值既不能重复也不能被修改。map 可以根据 key 值快速查找记录,复杂度在 log(n) 级别,如果有 1000 条记录,最多查找 10 次,如果有 1000000 条记录,最多查找 20 次。

map 容器的常用操作

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
#include <iostream>
#include <map>

using namespace std;

int main() {
// 定义Map集合变量
map<int, int> m;

// 第一种数据插入方式
m.insert(pair<int, int>(1, 2));
// 第二种数据插入方式(推荐)
m.insert(make_pair(3, 4));
// 第三种数据插入方式
m.insert(map<int, int>::value_type(5, 6));
// 第四种数据插入方式
m[7] = 8;

// 第一种方式遍历Map集合
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key = " << it->first << " , " << it->second << endl;
}

cout << endl;

// 第二种方式遍历Map集合
for (auto it = m.begin(); it != m.end(); it++) {
cout << "key = " << it->first << " , value = " << it->second << endl;
}

cout << endl;

// 获取指定的Key
map<int, int>::iterator item = m.find(5);
cout << "key = " << item->first << " , value = " << item->second << endl;

cout << endl;

// 第一种方式判断Key是否存在
// 如果Key存在,find()函数会返回Key对应的迭代器,如果Key不存在,find()函数会返回尾后迭代器end()
if (m.find(100) == m.end()) {
cout << "key " << 100 << " not exist" << endl;
}

cout << endl;

// 第二种方式判断Key是否存在
// count()函数用于统计Key值在Map中出现的次数,Map的Key是不允许重复的,因此如果Key存在会返回1,不存在会返回0
if (m.count(5) == 1) {
cout << "key " << 5 << " existed" << endl;
}

cout << endl;

// 删除指定的Key
m.erase(7);
for (auto it = m.begin(); it != m.end(); it++) {
cout << "key = " << it->first << " , value = " << it->second << endl;
}

}

程序运行输出的结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
key = 1 , 2
key = 3 , 4
key = 5 , 6
key = 7 , 8

key = 1 , value = 2
key = 3 , value = 4
key = 5 , value = 6
key = 7 , value = 8

key = 5 , value = 6

key 100 not exist

key 5 existed

key = 1 , value = 2
key = 3 , value = 4
key = 5 , value = 6

参考资料