C++字符串、向量、迭代器和数组
【C++学习】以w3cschool的教程为框架,记录总结C++Primer第五版的学习内容。
C风格字符串
-
字符串实际上是使用 null 字符 ‘’ 终止的一维字符数组;
char greeting[6] = {'H', 'e', 'l', 'l', 'o', ''}; char greeting[] = "Hello";
序号 函数 目的 1 strcpy(s1, s2); 复制字符串 s2 到字符串 s1。 2 strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。 3 strlen(s1); 返回字符串 s1 的长度。 4 strcmp(s1, s2); 如果 s1 和 s2 是相同的,则返回 0;如果 s1<s2 则返回小于 0;如果 s1>s2 则返回大于 0。 5 strchr(s1, ch); 返回一个指针,指向字符串 s1 中字符 ch 的第一次出现的位置。 6 strstr(s1, s2); 返回一个指针,指向字符串 s1 中字符串 s2 的第一次出现的位置。
标准库类型string
-
标准库类型string表示可变长的字符序列,必须包含string头文件,且string定义在命名空间std中:
#include<string> using std::string;
-
定义与初始化
int main(void) { string s1; // 默认初始化: 空串 string s2(s1); // 直接初始化 string s2 = "tomocat"; // 拷贝初始化, 等价于s2("tomocat") string s3 = s2; // 拷贝初始化,s3是s2的副本, 等价于s3(s2) string s4(5, 'a'); // 直接初始化,5个a组成的字符串 }
-
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 判断不等 < , <= , > , >= 利用字符在字典中的顺序进行比较 stoi (s1) 将s1转化为int类型 (ctoi同理,可用于s1中的字符) - 如果要读取多个string对象:
int main(void) { std::string word; while (std::cin >> word) // 遇到文件结束标记或者非法输入时退出循环 { std::cout << word << std::endl; } while (getline(std::cin, line)) // 每次读入一整行, 直到到达文件末尾 { std::cout << line << std::endl; } return 0; } **处理string对象的单个字符**
- 字面值和string对象相加:当把string对象和字符字面值及字符串字面值混在一条语句使用时,必须确保每个加法运算符(+)的两侧的运算对象至少有一个是string:
string s1("hello"); string s4 = s1 + ","; //正确:把一个string对象和一个字面值相加 string s5 = "hello" +","; //错误:两个运算对象都不是string //正确:每个加法运算符都又一个运算对象是string string s6 = s1 + "," + "world"; string s7 = "hello"+"," +s1; //错误:不能把字面值直接相加,反过来是s1 + "hello" +",";就可以
-
处理string对象中的字符
- cctype头文件提供的字符操作函数:
函数名 功能 isalnum© 当c是字母或数字时为真 isalpha© 当c是字母时为真 iscntrl© 当c是控制字符时为真 isdigit© 当c是数字时为真 isgraph© 当c不是空格但可打印时为真 islower© 当c是小写字母时为真 isupper© 当c是大写字母时为真 ispunct© 当c是标点符号时为真(即c不是控制字符、数字、字母、可打印空白中的一种) isspace© 当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) tolower© 如果c是大写字母,输出对应的小写字母,否则原样输出c toupper© 如果c是小写字母,输出对应的大写字母,否则原样输出c -
遍历string中的字符
string对象的下标必须大于等于0而小于s.size(),使用超出此范围的下标将引发不可预知的错误;
最好使用C++11新标准提供的范围for循环语句;
最好设置下标类型为string::size_type;
#include <iostream> #include <string> int main() { std::string s = "tomocat"; // 传统方法 for (std::string::size_type i = 0; i != s.size(); ++i) { std::cout << s[i] << std::endl; } // C++11新标准: 范围for循环 for (auto c : s) { std::cout << c << std::endl; } }
标准库类型vector
-
标准库类型vector表示对象的集合,其中对象的类型均相同,vector也被称作容器。使用vector,必须包含如下头文件和using声明:
#include <vector> using std::vector;
-
定义和初始化类型为T的vector
初始化 含义 vector v1 初始化为空 vector v2 = v1 根据v1进行赋值初始化 vector v2(v1) 根据v1进行直接初始化 vector v3(n, val) 构造初始化,v3中有n个val vector v3(n) 构造初始化,v3中有n个T类型的默认值 vector v4 = {a, b, c…} 参数列表赋值初始化 vector v4{a, b,c…} 参数列表直接初始化 -
vector操作
操作 含义 v.empty() 如果 v 为空,则返回 true,否则返回 false v.size() 返回 v 中元素的个数 v.push_back(t) 在 v 的末尾增加一个值为 t 的元素 v[n] 返回 v 中位置为 n 的元素 v1 = v2 把 v1 的元素替换为 v2 中元素的副本 v1 == v2 如果 v1 与 v2 相等,则返回 true -
推荐初始化一个空的vector,然后利用push_back添加元素,这样做更高效;
-
当vector中所有元素均相同的时候,列表或构造初始化效率更高;
-
当循环体中有push_back()函数时候,不可以使用针对vector的范围for循环;
-
禁止使用下标为vector添加新元素,下标只能访问已有元素,不能添加新元素;
-
当两个vector中类型能够比较的时候才可以比较;
-
vector.size()的类型是vector::size_type而不是vector::size_type,一般用decltype()返回。
-
迭代器
-
迭代器是一种指向容器内元素的类型,其自身并不是容器内的元素:
vector<int>::iterator it1; string::const_iterator it2;
-
容器类型可以使用迭代器进行容器内元素的访问,C++中全部容器都支持迭代器访问,但只有少部分容器支持下标访问;
-
vector是容器,string不是容器,但string支持迭代器;
-
指向元素或者容器中最后一个元素的一下一个位置的迭代器是有效的,其他的是无效的。
-
-
要访问迭代器指向的对象时候,需要使用*来解引用:
(*it).empty(); //*it外面必须有(),指向的对象有成员函数时候。 it->empty(); //推荐用这种
-
迭代器的获取函数
vector<int> s; vector<int>::iterator a = s.begin(); vector<int>::iterator b = s.end(); vector<int>::const_iterator c = s.cbegin(); vector<int>::const_iterator d = s.cend();
- end和cend都是指向容器尾元素的下一个位置,不是尾元素;
- 由于尾迭代器没有指向实际元素,因此不可以对其解引用;
-
迭代器运算
- 单个迭代器可以使用
+
-
+=
-=
; - 两个迭代器可以使用
-
>
>=
等。但前提是他们参与运算的时候必须是指向的同一个容器的元素或者尾后。 - 两个迭代器使用
-
得到的距离类型是difference_type,它是一个带符号的整型数,可正可负。
- 单个迭代器可以使用
-
任何使用改变容器大小的操作(添加和删除元素)都会使得迭代器失效,因此使用了迭代器的循环体内,禁止添加或删除元素。
数组
-
定义和初始化内置数组
const unsigned sz = 3; int ia1[sz] = {0,1,2}; int a2[] = {0, 1, 2}; int a3[5] = {0, 1, 2}; // equivalent to a3[] = {0, 1, 2, 0, 0} string a4[3] = {"hi", "bye"}; // same as a4[] = {"hi", "bye", ""} int a5[2] = {0,1,2}; // error: too many initializers
-
数组的维度必须是一个常量表达式;
-
不允许复制和赋值;
-
默认情况下,类型修饰符从右至左依次绑定。
理解数组的声明,最好从数组的名字开始,按照由内向外的顺序阅读。即,首先阅读圆括号内的部分,接下来观察右边,最后观察左边。int *ptrs[10]; // 由十个指向 int 的指针组成的数组 int &refs[10] = /* ? */; // error: no arrays of references int (*Parray)[10] = &arr; // Parray 指向一个由十个整数组成的数组 int (&arrRef)[10] = arr; // arrRef 绑定到一个由十个整数组成的数组
-
-
访问数组元素
- 当使用变量表示数组下标时,通常应定义该变量的类型为
size_t
,size_t
是机器特定的无符号类型,可以保证足以表示内存中任何对象的大小,定义在cstddef
头文件中;
- 当使用变量表示数组下标时,通常应定义该变量的类型为
-
指针和数组
-
在大多数情况下,当使用数组时,编译器会自动将其替换为一个指向首元素的指针:
string *p2 = nums; // 等同于 p2 = &nums[0]
-
通常,数组上的操作实际上是指针上的操作:
int ia[] = {0,1,2,3,4,5,6,7,8,9}; auto ia2(ia); // ia2 为指向ia首元素的int* ia2 = 42; // error: ia2 为指针 auto ia2(&ia[0]); // now it's clear that ia2 has type int*
-
-
库函数 begin 和 end
int ia[] = {0,1,2,3,4,5,6,7,8,9}; int *beg = begin(ia); int *last = end(ia);
-
两指针相减的结果的类型是一个名为
ptrdiff_t
的库类型,定义在 cstddef 头文件:auto n = end(arr) - begin(arr);
-
-
多维数组
- C++中没有多维数组。通常称为多维数组的实际上是数组的数组;
- 可以使用范围 for 语句处理多维数组;