《Hello C++》4.2 string 类

在前面学习数组时,我们已经知道了如何存放一组相同类型的数据。现在我们再往前走一步,认识 C++ 中非常常用的一种字符串类型:std::string[1]

在前面的程序中,我们其实已经使用过 cincout 这样的标准库对象,只是当时还没有集中讨论“类、类型和对象”的关系。这一次,咱们会把 std::string 当作本节的主角,正式认识一种标准库类类型,并学习怎样创建和使用这种类型的对象。

std::string 专门用于表示和操作字符串。与 C 风格字符串相比,它能自行管理保存字符所需的存储空间,并提供赋值、拼接、查询长度等操作。使用者通常不需要亲自维护末尾的空字符 '\0',因此日常字符串处理会方便许多。

4.2.1 string 类简介

字符串就是一串字符的组合,比如名字、句子、路径、提示信息等,都是字符串。C++ 标准库提供了 std::string 类型来表示这类数据。

1
2
3
4
5
6
7
8
9
10
11
12
#include <iostream>
#include <string>

using namespace std;

int main()
{
string name = "Hello";
cout << name << endl;

return 0;
}

其中最关键的一条语句是 string name = "Hello";。它定义并初始化了一个名为 name 的变量,string 表示变量的类型,name 是变量名,"Hello" 是它的初始内容。由于 string 是类类型,因此 name 同时也是一个 string 对象。这里发生的是初始化,而不是先创建对象再进行赋值。

这里需要把几个容易混淆的词分开:

  • :用于规定一类对象可以保存什么数据、提供什么操作;
  • 类类型:由类所定义的类型;std::string 是标准库提供的一个类类型名称;
  • 对象:某个类型在程序中的具体实体;这里的 name 是一个 string 对象;
  • 变量:由声明引入名称的对象或引用;这里的 name 是一个有名称的对象,因此同时也是一个变量。

因此,不能说“string 是一个类的对象”。在这段代码里,string 表示类型,name 才是该类型的对象。对象和变量也不是互相排斥的称呼:从类型与对象的关系看,name 是对象;从代码通过名称使用它这一点看,name 是变量。

这是本书第一次把一个标准库类类型作为主要内容来学习,但并不是第一次使用对象:前面出现的 cincout 也是对象。

你现在先记住:std::string 表示一种类类型,name 是这种类型的一个对象,也是一个变量。第 7 章会进一步讲解类怎样定义类型,以及对象怎样根据类型创建出来。

4.2.2 字符串初始化

string 的初始化方式很灵活,常见写法有下面几种。

1
2
3
4
string a;
string b = "Hello";
string c("World");
string d{"C++"};

第一种是不带初值,表示先创建一个空字符串。第二种和第三种都很常见,第四种则是 C++11 之后更现代的写法。

如果你想创建一个空字符串,也可以直接这样写:

1
string empty = "";

初始化后,你就可以对字符串进行赋值、拼接、附加和输出等操作了。

4.2.3 字符串赋值

string 的赋值非常直接,和普通变量很像。

1
2
string name;
name = "Alice";

这表示先声明一个字符串变量,再把 "Alice" 赋给它。你也可以把一个 string 赋给另一个 string

1
2
3
string a = "Hello";
string b;
b = a;

这时,b 的内容就变成了 "Hello"

4.2.4 字符串拼接和附加

字符串最常见的用途之一,就是把几个片段拼起来。

1
2
3
string first_str = "Hello";
string second_str = " C++";
string full_str = first_str + second_str;

这里 + 表示拼接,最终 full_str 的内容就是 "Hello C++"

如果你想在原有字符串后面继续加内容,可以使用 +=,也就是附加。

1
2
string text = "Hello";
text += " world";

这时 text 的内容会变成 "Hello world"+= 的好处是很直观,意思就是“在原来的基础上继续加”。

你还可以连续拼接多个字符串:

1
string result = "Hello" + string(" ") + "C++";

4.2.5 string 类的其他操作

string 类除了能初始化、赋值和拼接之外,还有一些非常实用的操作。

例如,查看长度:

1
2
3
string s = "Hello";
cout << s.size() << endl;
cout << s.length() << endl;

size()length() 这两个成员函数通常都能返回字符串长度,很多时候可以互换使用。

你还可以访问某个位置上的字符:

1
char ch = s[1];

这表示取出字符串中的第二个字符。和数组类似,字符串下标也是从 0 开始的。

如果你想判断字符串是否为空,也可以这样写:

1
2
3
4
if (s.empty())
{
cout << "empty" << endl;
}

这些操作让 string 变得非常实用,因为它不像 C 风格字符串那样需要你自己去处理很多细节。

4.2.6 string 类 I/O

string 的输入和输出也很方便。

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
#include <string>

using namespace std;

int main()
{
string name;
cin >> name;
cout << name << endl;

return 0;
}

这里的 cin >> name 会读取一个单词,也就是说它会在遇到空格时停止。

如果你想读取包含空格的一整行文本,可以使用 getline()

1
2
string line;
getline(cin, line);

getline() 会把一整行内容都读进来,包括空格,直到遇到换行符为止。这一点在读取姓名、句子、地址等内容时特别有用。

4.2.7 其他形式的字符串字面值

前面我们经常看到 "Hello" 这种写法,它叫做字符串字面值[2]。在 C++ 中,字符串字面值还有一些其他形式。

比如原始字符串字面值[3]

1
cout << R"(C:\Users\Hello\Desktop)" << endl;

这种写法可以减少转义符号的烦恼。对于路径、正则表达式、带很多反斜杠的文本,它尤其方便。

还有宽字符串字面值[4]

1
wchar_t ws[] = L"Hello";

这类写法通常用于更宽字符集的处理。初学阶段你先知道它们存在就可以,后面如果真的遇到,再按需要深入学习。

4.2.8 小结

string 类让字符串处理变得比 C 风格字符串更自然,也更安全。你可以轻松地初始化、赋值、拼接、附加、读取和输出字符串,还可以用 size()empty() 之类的成员函数来管理它。

更重要的是,这一小节是你第一次比较正式地接触“类”的概念。你现在先不用急着把类想得太复杂,只要记住:string 是标准库提供的一个类,而你使用的 string 变量,就是这个类创建出来的对象。后面我们会专门花一章,把“类是什么、对象是什么、它们为什么这么重要”讲清楚。


  1. string 类,属于 C++ 标准库,用于表示和操作字符串。 ↩︎

  2. 字符串字面值,是直接写在代码中的字符串常量。 ↩︎

  3. 原始字符串字面值(raw string literal),使用 R"(...)" 形式。 ↩︎

  4. 宽字符串字面值,用于更宽字符集的字符串表示。 ↩︎