该笔记包括string
、vector
、迭代器和数组。
目录
标准库类型string
#include <string> using std::string;
定义和初始化string
对象
string s1; // 默认初始化,s1是一个空字符串 string s2(s1) // s2是s1的副本 string s2 =s1; // 等价于s2(s1),s2是s1的副本 string s3("hiya"); // s3是字面值"hiya"的副本,除了字面值后面的那个空字符外 string s3 = "hiya"; // 等价于s3("hiya"),s3是该字符串字面值的副本 string s4(10,'c'); // s4的内容是cccccccccc
如果使用等号初始化一个变量,实际执行的是拷贝初始化,编译器把等号右边的初始值拷贝到新创建的对象中去;反之,如果不使用等号,则执行的是直接初始化。
string
对象上的操作
代码 | 操作 |
---|---|
os<<s | 将s写到输出流os当中,返回os |
is>>s | 从is读取字符串赋给s,字符串以空白分隔,返回is |
getline(is, s) | 从is中读取一行赋给s,返回is |
s.empty() | s为空返回ture,否则返回is |
s.size() | 返回s中字符的个数 |
s[n] | 返回s中第n个字符的引用,位置从0计起 |
s1+s2 | 返回s1和s2连接后的结果 |
s1=s2 | 用s2的副本代替s1中原来的字符 |
s1==s2 & s1 !=s2 | 判断字符串是否完全一样,对大小写敏感 |
<,<=,>,>= | 利用字符在字典中的顺序进行比较,且对字母的大小写敏感 |
读写string
对象
int main(){ string s; // 空字符串 cin >> s; // 忽略开头的空白,从第一个真正的字符开始将string对象读入s,遇到空白停止 cout << s << endl; //输出s return 0; };
使用getline
读取一整行
int main(){ string line; //每次读取一整行,直至到达文件末尾 while (getline(cin, line)) //跳过空行 if (!line.empty()) //每次读取一整行,输出其中超过80个字符的行 if (line.size() > 80) cout << line << endl; return 0; };
string::size_type
类型
比较string
对象
- 如果两个
string
对象的长度不同,而且较短string
对象的每个字符都与较长字符string
对象对应位置上的字符相同,就说较短string
对象小于较长string
对象。 - 如果两个
string
对象在某些对应的位置上不一致,则string
对象比较结果其实是string
对象中第一对相异字符比较的结果。
string str = "Hello"; string phrase = "Hello World"; string slang = "Hiya";
由规则可知:slang>phrase>str。
处理string
对象中的字符
cctype头文件
代码 | 操作 |
---|---|
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不是控制字符、数字、字母、可打印空白中的一种 |
isspase(c) | 当c是空白时为真(即c是空格、横向制表符、纵向制表符、回车符、换行符、进纸符中的一种) |
isupper(c) | 当c是大写字母时为真 |
isxdigit(c) | 当c是十六进制数字时为真 |
tolower(c) | 如果c是大写字母,输出对应的小写字母;否则原样输出c |
toupper(c) | 如果c是小写字母,输出对应的大写字母;否则原样输出c |
使用范围for语句处理每个字符
for (declaration : expression) statement
其中,expression部分是一个对象,用于表示一个序列;declaration部分负责定义一个变量,该变量将被用于访问序列中的基础元素。每次迭代,declaration部分的变量将会被初始化为expression部分的下一个元素值。
例子
string s("Hello World!!!"); //decltype返回s.size的类型 deltype(s.size()) punct_cnt = 0; //统计s中标点符号的数量 for (auto c : s) if (ispunct(c)) ++puct_cnt; cout << punct_cnt << " punctuation characters in " << s <<endl;
使用范围for语句改变字符串中的字符
string s("Hello World!!!"); //转换成大写形式 for (auto &c : s) //c是引用! c = toupper(c); //c是一个引用,因此赋值语句将改变s中字符的值 cout << s << endl;
使用下标进行迭代
//依次处理s中的字符直至我们处理完全部字符或者遇到一个空白 for (deltype(s.size()) index = 0; // index必须大于等于0,且小于s.size() // &&运算符仅会在左侧对象为真时才会检查右侧运算情况 index != s.size() && !isspace(s[index]); ++index) s[index] = toupper(s[index]);
标准库类型vector
#include <vector> using std::vector;
定义和初始化vector
对象
代码 | 操作 |
---|---|
vector\ v1 | v1是一个空vector,它潜在的元素是T类型的,执行默认初始化 |
vector\ v2(v1) | v2中包含有v1所有元素的副本 |
vector\ v2=v1 | 等价于v2(v1) |
vector\ v3(n,val) | v3包含了n个重复的元素,每个元素的值都是val |
vector\ v4(n) | v4包含了n个重复地执行了值初始化的对象 |
vector\ v5{a,b,c…} | v5包含了初始值个数的元素,每个元素被赋予了相应的初始值 |
vector\ v5={a,b,c…} | 等价于v5{a,b,c…} |
//默认初始化 vector<int> ivec; vector<int> ivec2(ivec); vector<int> ivec3 = ivec; //列表初始化 vector<string> v1{"a", "an", "the"}; //创建指定数量的元素 vector<int> ivec(10, -1); //值初始化 vector<int> ivec(10); //10个元素,每个都初始化为0 vector<string> svec(10); //10个元素,每个都是空string对象
向vector
对象中添加元素
使用push_back
向vector中添加元素:
vector<int> v2; // 空vector对象 for (int i = 0; i != 100; ++i) v2.push_pack(i); // 依次把整数值放到v2尾端
Warning from Primer:范围for语句体内不应改变其所遍历序列的大小。
其他vector
操作
代码 | 操作 |
---|---|
v.empty() | 如果v不含有任何元素,返回真;否则返回假 |
v.size() | 返回v中元素的个数 |
v.push_back(t) | 向v的尾端添加一个值为t的元素 |
v[n] | 返回v中第n个位置上元素的引用 |
v1=v2 | 用v2中元素的拷贝替换v1中的元素 |
v1={a, b, c…} | 在列表中元素的拷贝替换v1中的元素 |
v1==v2, v1!=v2 | v1和v2是否相等 |
<,<=,>,>= | 以字典顺序进行比较 |
范围for语句访问vector
对象中元素
vector<int> v{1,2,3,4,5,6,7,8,9}; for (auto &i : v) i *= i; for (auto i : v) cout << i << " "; cout << endl;
计算vector内对象的索引
vector<unsigned> scores(11, 0); unsigned grade; while (cin >> grade) { if (grade <= 100) ++scores[grade/10]; }
迭代器介绍
使用迭代器
begin
和end
成员
// 由编译器决定b和e的类型; // b表示v的第一个元素,e表示v尾元素的下一个位置 auto b = v.begin(), e = v.end(); //b和e的类型相同
Note:如果容器为空,则
begin
和end
返回的是同一个迭代器,都是尾后迭代器。
迭代器运算符
代码 | 操作 |
---|---|
*iter | 返回迭代器iter所指元素的引用 |
iter->mem | 解引用iter并获取该元素的名为mem的成员,等价于(*iter).mem |
++iter | 令iter指示容器中的下一个元素 |
–iter | 令iter指示容器中的上一个元素 |
iter1 == iter2 | 判断两个迭代器是否相等(不相等),如果两个迭代器指示的是同一个元素或者它们是同一个容器的尾后迭代器,则相等;反之不相等 |
举例:
string s("some string"); if (s.begin() != s.end()) { // 确保s非空 auto it = s.begin(); //it表示s的第一个字符 *it = toupper(*it); //将当前字符改成大写形式 } // 将字符串第一个单词全部改为大写形式 // 依次处理s的字符直至处理完或遇到空白 for (auto it = s.begin(); it != s.end && !isspace(*it); ++it) *it = toupper(*it); //将当前字符改成大写形式
迭代器类型
vector<int>::iterator it; //it能读写vector<int>的元素 string::iterator it2; //it2能读写string对象中的字符 vector<int>::const_iterator it3; //it3只能读元素,不能写元素 string::const_iterator it4; //it4只能读字符,不能写字符
cbegin
和cend
不论vector(string)对象本身是否为常量,返回值都是const_iterator
vector<int> v; const vector<int> cv; auto it1 = v.begin(); //it1的类型是vector<int>::iterator auto it2 = cv.begin(); //it2的类型是vector<int>::const_iterator auto it3 = v.cbegin(); //it3的类型是vector<int>::const_iterator
结合解引用和成员访问操作
(*it).empty() //解引用it,然后调用结果对象的empty成员
C++定义了箭头运算符(->
)。箭头运算符将解引用和成员访问两个操作结合在一起,也就是说,it->mem
和(*it).mem
表达的意思相同。
// 依次输出text的每一行,直至遇到第一个空白为止 for (auto it = text.cbegin(); it != text.cend() && !it->empty(); ++it) cout << *it << endl;
迭代器运算
使用迭代器运算进行二分查找
// text必须是有序的 // beg和end表示我们的搜索范围 auto beg = text.begin(), end = text.end(); auto mid = text.begin() + text.size()/2; //初始状态下的中间点 //当还有元素尚未检查并我们还没有找到sought时执行循环 while (mid != end && *mid != sought) { if (sought < *mid) end = mid; else beg = mid+1; mid = beg + (end - beg)/2; }
数组
定义和初始化
unsigned cnt = 42; //不是常量表达式 constexpr unsigned sz = 42; //常量表达式 int arr[10]; //含10个整数的数组 int *parr[sz]; //含有42个整型指针的数组 string bad[cnt]; //错误:cnt不是常量表达式 string strs[get_size()]; //当get_size是constexpr时正确;否则错误
显式初始化数组元素
const unsigned sz = 3; int ia1[sz] = {0, 1, 2}; //含有3个元素的数组, int a2[] = {0, 1, 2}; //维度时3的数组 int a3[5] = {0, 1, 2}; //等价于a3[] = {0, 1, 2, 0, 0} string a4[3] = {"hi", "bye"}; //等价于a4[] = {"hi", "bye", ""} int a5[2] = {0, 1, 2}; //错误:初始值过多
字符数组的特殊性:可以用字符串字面值进行初始化,但结尾处的空字符也会被拷贝进字符数组。
char a1[] = {'C', '+', '+'}; //列表初始化,没有空字符 char a2[] = {'C', '+', '+', '\0'}; //列表初始化,含有显式的空字符 char a3[] = "C++"; //自动添加表示字符串结尾的空字符 const char a4[6] = "Daniel"; //错误:没有空间可存放空字符
不允许拷贝和赋值:不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为其他数组赋值。
int a[] = {0, 1, 2}; //含有3个整数的数组 int a2[] = 1; //错误:不允许使用一个数组初始化另一个数组 a2 = a; //错误:不能把一个数组直接赋给另一个数组
理解复杂的数组声明
int *ptrs[10]; //ptrs是含有10个整型指针的数组 int (*Parray)[10] = &arr; //Parray指向一个含有10个整数的数组 int (&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数字 int *(&arry)[10] = ptrs; //arry是数组的引用,该数组含有10个指针
下标访问和遍历
unsigned score[11] = {}; unsigned grade; while (cin >> grade){ if (grade <= 100) ++scores[grade/10]; } for (auto i : scores) cout << i << " "; cout <<endl;
指针和数组
string nums[] = {"one", "two", "three"}; string *p = &nums[0]; //p指向nums的第一个元素 //数组特性,在很多用到数组名字的地方,编译器会自动将其替换为一个指向数组首元素的指针 string *p2 = nums; //等价于p2 = &nums[0]
指针也是迭代器
int arr[] = {0,1,2,3,4,5,6,7,8,9}; int *p = arr; //p指向arr的第一个元素 ++p; //p指向arr[1] int *e = &arr[10]; //指向arr尾元素的下一位置的指针 //遍历 for (int *b = arr; b != e; ++b) cout << *b << endl; //输出arr的元素
标准库函数begin
和end
int ia[] = {0,1,2,3,4,5,6,7,8,9}; int *beg = begin(ia); //指向ia首元素的指针 int *last = end(ia); //指向arr尾元素的下一位置的指针
指向数组的指针可以执行与迭代器相似的运算
constexpr size_t sz = 5; int arr[sz] = {1,2,3,4,5}; int *ip = arr; int *p2 = ip + 4; //ip2指向arr的尾元素arr[4]
解引用和指针运算的交互
int ia[] = {0,2,4,6,8}; int last = *{ia + 4}; //last初始化为8
下标和指针
int ia[] = {0,2,4,6,8}; int i = ia[2]; int *p = ia; i = *(p + 2); //等价于i = ia[2] int *p = &ia[2]; //p指向索引为2的元素 int j = p[1]; //p[1]等价于*(p + 1) int k = p[-2]; //p[-2]是ia[0]表示的那个元素
多维数组
多维数组实际上是数组的数组。
int ia[3][4]; int arr[10][10][10] = 0; int ia[3][4] = { {0,1,2,3}, {4,5,6,7}, {8,9,10,11} }; int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //与上一句是等价的 //显式地初始化每行首元素 int ia[3][4] = {{0},{4},{8}}; //显示地初始化第1行,其他元素执行值初始化 int ix[3][4] = {0, 3, 6, 9};
多维数组的下标引用
ia[2][3] = arr[0][0][0]; int (&row)[4] = ia[1]; //把row绑定到ia的第二个4元素数组上
使用范围for语句处理多维数组
size_t cnt = 0; for (auto &row : ia) //对于外层数组的每一个元素 for (auto &col : row) { //对于内层数组的每一个元素 col = cnt; ++cnt; }
指针和多维数组
int ia[3][4]; //大小为3的数组,每个元素是含有4个整数的数组 int (*p)[4] = ia; //p指向含有4个整数的数组 p = &ia[2]; //p指向ia的尾元素
指针遍历
//p指向ia的第一个数组 for (auto p = begin(ia); p != end(ia); ++p) { //q指向内层数组的首元素 for (auto q = begin(*p); q != end(*p); ++q) cout << *q << ' '; cout << endl; }