三、字符串、向量和数组
3.1 命名空间的 using 声明
-
作用域操作符:
::
的含义是编译器应从操作符左侧名字所示的作用域中寻找右侧那个名字 -
using
声明:有了using
声明就无须专门的前缀也能使用所需的名字了#include <iostream> using std::cout; int main() { cout << "Hello World"; return 0; }
-
每个名字都需要独立的
using
声明:#include <iostream> using std::cin; using std::cout; using std::endl; int main() { cout << "Enter two numbers:" << endl; int v1, v2; cin >> v1 >> v2; cout << "The sum of " << v1 << " and " << v2 << " is " << v1 + v2 << endl; return 0; }
-
头文件不应包含
using
声明:头文件的内容会拷贝到所有引用它的文件中去,对于某些程序来说,由于不经意间包含了一些名字,反而可能产生始料未及的名字冲突
3.2 标准库类型 string
标准库类型 string
表示可变长的字符序列,使用 string
类型必须首先包含 string
头文件。作为标准库的一部分,string
定义在命名空间 std
中。
3.2.1 定义和初始化 string 对象
-
常用的初始化
string
对象的方式:#include <string> using std::string; int main() { string s1; // "" string s2 = s1; // s2 是 s1 的副本 string s3 = "hello"; // s3 是该字符串字面值的副本 string s4(10, 'c'); // cccccccccc }
表达式 说明 string s1;
默认初始化, s1
是一个空串string s2(s1);
s2
是s1
的副本string s2 = s1;
等价于 s2(s1)
string s3("value");
s3
是字面值"value"
的副本,除了最后的空字符'\0'
外string s3 = "value";
等价于 s3("value")
string s4(n, 'c');
把 s4
初始化为由连续n
个字符c
组成的串 -
直接初始化和拷贝初始化:如果使用等号初始化一个变量,实际上执行的是拷贝初始化,编译器把等号右侧的初始值拷贝到新创建的对象中去。与之相反,不使用等号时执行的是直接初始化
3.2.2 string 对象上的操作
操作 | 含义 |
---|---|
os << s |
将 s 写到输出流 os 当中,返回 os |
is >> s |
从 is 中读取字符串赋给 s ,字符串以空白分隔,返回 is |
getline(is, s) |
从 is 中读取一行赋给 s ,返回 is |
s.empty() |
s 为空串返回 true ,否则返回 false |
s.size() |
返回 s 中字符的个数 |
s[n] |
返回 s 中第 n 个字符的引用,位置 n 从 0 开始计数 |
s1 + s2 |
返回 s1 和 s2 连接后的结果 |
s1 = s2 |
用 s2 的副本代替 s1 中原来的字符 |
s1 == s2 |
判断 s1 和 s2 中所含的字符是否完全一样 |
s1 != s2 |
判断 s1 和 s2 中所含的字符不完全一样 |
< 、<= 、> 、>= |
利用字符在字典中的顺序进行比较,且大小写敏感 |
-
读写
string
对象:#include <iostream> #include <string> int main() { std::string s; // 将标准输入的内容读取到 s 中, // string 对象会自动忽略开头的空白,一直读取直到遇到下一处空白停止 std::cin >> s; std::cout << s << std::endl; return 0; }
-
读取未知数量的
string
对象:#include <iostream> #include <string> int main() { std::string word; while (std::cin >> word) { std::cout << word << std::endl; } return 0; }
-
使用
getline
读取一整行:#include <iostream> #include <string> int main() { std::string line; /* * getline 函数从给定的输入流中读入内容,直到遇到换行符为止(注意换行符也被读进来了), * 然后把所读的内容存入到那个 string 对象中去(注意不存换行符) */ while (getline(std::cin, line)) { std::cout << line << std::endl; } return 0; }
-
string
的empty
和size
操作:#include <iostream> #include <string> int main() { std::string line; while (getline(std::cin, line)) { if (!line.empty() && line.size() > 10) { std::cout << line << std::endl; } } return 0; }
-
string::size_type
类型:size()
函数的返回值是string::size_type
类型。string
类及其他大多数标准库类型都定义了几种配套的类型,这些配套类型体现了标准库类型与机器无关的特性,类型size_type
即是其中的一种。 -
比较
string
对象:- 如果两个
string
对象的长度不同,而且较短string
对象的每个字符都与较长string
对象对应位置上的字符相同,就说较短string
对象小于较长string
对象 - 如果两个
string
对象在某些对应的位置上不一致,则string
对象比较的结果其实是string
对象中第一对相异字符比较的结果
#include <iostream> #include <string> int main() { std::string str = "Hello"; std::string phase = "Hello World"; std::string slang = "Hiya"; // slang > phase > str std::cout << (str > phase) << std::endl; // 0 std::cout << (str > slang) << std::endl; // 0 std::cout << (phase > slang) << std::endl; // 0 return 0; }
- 如果两个
-
为
string
对象赋值:一般来说,在设计标准库类型时都力求在易用性上向内置类型看齐std::string str1(10, 'c'), str2; str1 = str2; // 赋值,用 str2 的副本替换 str1 的内容,此时二者均为空串
-
两个
string
对象相加:std::string s1 = "hello, ", s2 = "world\n"; std::string s3 = s1 + s2; // s3 = "hello, world\n" s1 += s2; // s1 = s1 + s2
-
字面值和
string
对象相加:即使一种类型并非所需,我们也可以使用它,不过前提是该种类型可以自动转换成所需的类型。因为标准库允许把字符字面值和字符串字面值转换成string
对象,所以在需要string
的地方就可以使用这两种字面值来替代std::string s1 = "hello", s2 = "world"; // string 与字符或字符串字面值混加时,需保证加号一侧是 string 对象 std::string s3 = s1 + ", " + s2 + '\n'; std::string s4 = s1 + ", "; // 正确 std::string s5 = "hello" + ", "; // 编译错误 std::string s6 = s1 + ", " + "world"; // 正确: (s1 + ", ") 返回 string 对象 std::string s7 = "hello" + ", " + s2; // 编译错误
因为某些历史原因,也为了与
C
兼容,所以C++
语言中的字符串字面值并不是标准库类型string
的对象。切记,字符串字面值与string
是不同的类型。
3.2.3 处理 string 对象中的字符
在 cctype
头文件中定义了一组标准库函数用于处理 string
对象中的字符:
函数名 | 功能 |
---|---|
isalnum(c) |
当 c 是字母或数字时为真 |
isalpha(c) |
当 c 是字母时为真 |
iscntrl(c) |
当 c 是控制字符时为真 |
isdigit(c) |
当 c 是数字时为真 |
isgraph(c) |
当 c 不是空格但可打印时为真 |
islower(c) |
当 c 是小写字母时为真 |
isprint(c) |
当 c 是可打印字符时为真(即 c 是空格或 c 具有可视形式) |
ispunct(c) |
当 c 是标点符号时为真(即 c 不是控制字符、数字、字母、可打印空白中的一种) |
isspace(c) |
当 c 是空白时为真(即 c 是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) |
isupper(c) |
当 c 是大写字母时为真 |
isxdigit(c) |
当 c 是十六进制数字时为真 |
tolower(c) |
将 c 转换为小写字母 |
toupper(c) |
将 c 转换为大写字母 |
-
使用
C++
版本的C
标准库头文件:C
语言的头文件形如name.h
,C++
将这些文件命名为cname
。也就是去掉了.h
后缀,而在文件名name
之前添加了字母c
,表示这是一个属于C
语言的标准库头文件。cname
头文件中定义的名字从属于命名空间std
,而定义在.h
中的头文件则不然。 -
使用基于范围的
for
语句:C++11
新标准提供的范围for
语句非常适用于处理string
对象中的每个字符#include <iostream> #include <string> #include <cctype> int main() { std::string s("Hello World!!!"); // punct_cnt 的类型与 s.size 的返回类型一样 decltype(s.size()) punct_cnt = 0; for (auto c: s) { if (ispunct(c)) { ++punct_cnt; } } std::cout << punct_cnt << " punctuation characters in " << s << std::endl; return 0; }
-
使用范围
for
语句改变字符串中的字符:#include <iostream> #include <string> #include <cctype> int main() { std::string str("hello world!!!"); for (auto &c: str) { c = toupper(c); // c 是一个引用,因此赋值语句将改变 s 中字符的值 } std::cout << str << std::endl; return 0; }
-
使用下标运算符实现只处理一部分字符:下标运算符
[]
接收的输入参数是string::size_type
类型的值,这个参数表示要访问的字符的位置,返回值是该位置上的字符的引用#include <iostream> #include <string> #include <cctype> int main() { std::string str