C++ 杂记之三从基础到进阶

大纲

容器

string 容器

string 容器的概念

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

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

string 的构造函数

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

字符串头文件说明

  • 在标准 C++ 中,使用 std::string 必须显式包含 <string> 头文件。任何在未包含 <string> 的情况下仍然能够使用 std::string 的代码,都依赖于标准库实现的间接包含行为,属于非标准、不可移植的用法。
  • 一些标准库实现(例如 libstdc++)在 <iostream> 等头文件的内部实现中,可能间接包含了 <string>,从而使代码在特定编译器和版本下 "看起来可以正常工作"。然而,这种行为是实现细节,不受 C++ 标准保证,随着编译器、标准库版本或编译选项(如不同的 -std= 标准级别)变化,代码都有可能无法通过编译。

string 的初始化方式

  • C 语言中,字符串的初始化方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 使用字符串字面量初始化:末尾自动追加 '\0',其余元素补 0
char str1[100] = "C++";

// 等价于上面 s1 的写法效果,显式写出 '\0',其余元素补 0
char str2[100] = {'C', '+', '+', '\0'};

// 不指定字符串长度,让编译器自动推导,实际大小是 4(包括末尾的 '\0')
char str3[] = "C++";

// 使用字符列表初始化(末尾必须要有 '\0',否则不是字符串)
char str4[] = {'C', '+', '+', '\0'};

// 部分初始化:其余元素补 0(非字符串语义,但结果满足 '\0' 结尾)
char str5[100] = {'C', '+', '+'};
  • C++ 中,字符串的初始化方式
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
// 默认初始化,s1 是空字符串,表示里面没有字符
string s1;

// 将指定的字符串内容拷贝到 s2 所代表的一段内存中,拷贝时不包括字符串末尾的 '\0'
string s2 = "I Love C++";

// 跟上面字符串 s2 的写法效果一样
string s3("I Love C++");

// 列表初始化
string s4{"I Love C++"};

// 将字符串 s2 的内容拷贝到 s4 所代表的一段内存中
string s5 = s2;

// 将 s5 初始化为连续 5 个字符 'a' 组成的字符串(aaaaa),可能会在系统内部创建临时对象
string s6(5, 'a');

// 从 s2 的下标 2(第 3 个字符)开始,连续拷贝到字符串末尾,构造新的字符串
string s7(s2, 2);

// 从 s2 的下标 2(第 3 个字符)开始,连续拷贝 4 个字符,构造新的字符串
string s8(s2, 2, 4);

// 通过字符数组(必须有一个元素是 '\0',否则会出现未定义行为)初始化字符串
char buf[] = {'a', 'b', 'c', '\0'};
string s9(buf);

从 string 取得 char*

string 取得 char*,可以使用:

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

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

string 拷贝到 char*

string 拷贝到 char* 指向的内存空间,可以使用:

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

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

从 string 与范围 for 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
string s10 = "hello c++";
// 通过范围 for 遍历字符串所有元素
for (char c : s10) {
cout << c;
}
cout << endl;

// 通过范围 for 和引用遍历字符串所有元素,并更改其元素的值
for (char& c : s10) {
c = toupper(c);
cout << c;
}
cout << endl;

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

1
2
hello c++
HELLO C++

vector 容器