字符串、向量和数组
1. 命名空间的using 声明
-
简单的声明方式:
using namespace::name; //如using std::cin;
-
可放多行
-
头文件中含有using后每个都会声明了
1.1 string
位于#include里的std中
-
定义和初始化string对象
=
:拷贝初始化- 不用
=
:直接初始化
string s1; //空的 string s2 = s1; //s1的副本 string s3 = "yes"; //同上 string s33("yes"); //同上 string s4(10, 'c'); //s4 内容是十个c
-
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[n] //返回s第n个字符的引用 s1+s2 //返回s1和s2连接的结果 s1=s2 s1==s2 s1!=s2 < <= > >= //利用字典序比较,对大小写敏感
-
读取未知对象
int main() { string s; while (cin >> s) //反复读取直到文件结束符 cout << s << endl; return 0; }
1.1.1 string::size_type 类型
size()
函数返回的是string::size_type
类型值- 无符号值
- 所以注意
(s.size() < n)
时如果n是负值 判断结果几乎是true;
1.1.2 比较
-
按字典序大小比较
-
前面相同则短的小
-
运算是从左到右且两个之间必须有一个是string型
2. 处理string类型对象的字符
常用到的标准库在头文件
#include <cctype>
中
-
包含函数
isalnum(c) //当c是字母或数字为真 isalpha(c) //当c是字母为真 iscntrl(c) //当c是控制字符时为真 isdigit(c) //当c时数字为真 isgraph(c) //当c不是空格但可打印为真 islower(c) //当c时小写字母时为真 upper isprint(c) //当c是可打印字符为真 ispunct(c) //当c是标点符号为真 isspace(c) //当c为空白是真 stoll(c) //转数字 tolower(c) //转小写 upper
c++11新语句for(range for) 遍历每个元素并对值进行操作
-
for
for (declaration : expression) statement expression 部分是对象,表示一个序列 declaration 部分负责定义一个变量,该变量用于访问序列的基础元素,每次迭代变成expression部分的下一个元素 string str("some string"); for (auto c : str) //c初始为str的元素 cout << c << endl; for (auto &c : str) //c引用了str的元素,所以可以更改值 c+=1; cout << str << endl;
-
调用下标时不要越界
3. vector
常被称为容器
#include <vector> std
中c++有类模板和函数模板
vector是一个类模板, 模板本身可以看作一份说明,编译器根据模板创建类或者函数的过程称实体化
vector<int> iv; //存放int
vector<vectot<string>> file; //向量元素是vector对象
-
引用不是对象,所以不包含在
vector
中 -
初始化
vector<T> v1; //空的 vector<T> v2(v1); //含有v1所有元素的副本 vector<T> v2 = v1; //同上 vector<T> v3(n, val); //包含m个重复元素,每个值都是val vector<T> v4(n); //包含n个重复性的初始化对象 vector<T> v5{a,b,c...}; //初始化为各个值 vector<T> v5={a,b,c...}; //初始化为各个值
-
元素的类型必须对应
-
初始化自定义元素列表只能是花括号
-
当花括号元素类型不对应时,会自动考虑默认值初始化
vector<string> ve{10} //因为10int型考虑成,初始10个空元素 vector<string> ve{10,"123"} //因为10int型考虑成,初始10个"123"元素
-
3.1 向vector添加元素
-
push_back()
: 负责把元素压到尾端 -
注意范围for语句不能改变遍历序列的大小
-
vector
:其他常用操作v.empty() 判断空 v.size() 取元素个数 v.push_back() 压入元素 v[n] 返回第n个元素的引用 v1 = v2 拷贝 v1 = {a, b, c...} 拷贝 v1 != v2 v1 == v2 <, <=, >, >= 以字典序进行比较
-
同样可以用
auto
-
size
返回值为size_type
类型 必须vector<int>::size_type; //正确 vector::size_type; //错误
-
元素相同容量少的比容量多的小
-
大小关系与元素值定义的关系决定
3.1 vector 内元素的索引
与string 下标索引一样
- 不能用下标形式添加元素
- 会导致所谓的缓冲区溢出
4. 迭代器
iterator
迭代器
- 所有标准库都可以使用迭代器
- 提供对对象的间接访问
- 有效迭代器
- 指向某个元素
- 容器中尾元素的下一个位置
- 其余都时无效
4.1 使用迭代器
和指针不一样的时,不是用取地址符
拥有迭代器的类型
- 成员
begin
和end
成员 第一个元素或者最后一个元素的下一个位置
4.1.1 运算符
-
标准容器迭代器的运算符
*iter 返回迭代器iter所指向的元素 iter->mem 解引用iter并获取该元素的名为mem的成员,(*iter).mem相同 ++iter 指向下一个元素 --iter 指向上一个元素 == != 判断是否相同指向相同元素,并不是指元素值
-
和指针类似
- 可直接对值进行操作
4.1.2 迭代器类型
-
iterator
能读写vector<int>::iterator it; string::iterator it2;
-
const_iterator
只能读和常量指针差不多
vector<int>::const_iterator it3;
4.1.3 begin和end
运算符
-
如果对象是常量返回const_iterator;
-
如果不是返回iterator
vector<int> v; const vector<int> v2; auto it1 = v.begin(); auto it2 = v2.begin(); const_iterator
c++11 为const_iterator 引入cbegin 和cend
无论变量是什么都返回const_iterator
-
调用类对象的成员函数
- 必须要加(*it).name(); 括号
- 加括号和新号才是访问元素本身
- 当然用
->
也可以直接使用
4.1.4 容易让迭代失效的操作
- 不能操作任何改变对象容量的操作 如
push_back
4.2 迭代器运算
在string和vector的迭代器中提供了额外更多的运算符,可跨越多个元素,支持运算关系
-
所支持的关系表
操作 含义 iter+n 迭代器加上一个整数依然是迭代器相当于移动n个位置返回值 iter-n 同上类似 iter+=n 加上赋值语句 iter-=n 同上 iter1-iter2 得到距离的differece_type类型 > >= < <= -
difference_type
的带符号的整型数 距离 两个迭代器相减获得的结果类型- string 和 vector都定义了
-
用迭代器写的二分
auto beg = t.begin(), end = t.end(); //t的内容有序 auto mid = t.begin()+(end-beg)/2; while (mid != end && *mid != ans) { //查找ans元素 if (ans < *mid) end = mid; else beg = mid; mid = beg+(end-begin)/2; }
5. 数组
类似标准库类型vector 的数据结构
-
不同点数组大小固定
-
定义和初始化
unsigned cn = 42; constexpr unsigned sz = 42; int arr[10]; //数组10个整数 int *parr[sz]; //含有42个整数指针 string dba[cnt]; //错误 不能用变量表达式 string str[get_size()]; //当get_size() 是constexpr时正确
- 数组定义时必须规定类型,不能用auto判断
- 不存在引用数组
-
显示初始化数组元素
列表初始化。如果忽略维度。
编译器会根据初始值判断,相反如果声明知明维度,那初始值的总额不能超过维度大小。如果维度大于初始值则用靠前的元素,剩下的元素被初始化成默认值
const unsigned sz = 3;
int ial[sz] = {0, 1, 2};
int a2[] = {0, 1, 2};
int a3[5] = {0, 1, 2};
string a4[3] = {"hi", "bye"};
int a5[2] = {0,1,2}; // 错误
-
字符数组额外提供一个初始化方式
char a[] = "c++"; //自动添加结束符的空字符 char a4[3] = "c++"; //错误,没空间存放空字符
-
数组不能直接拷贝和赋值给另一个数组
-
稍微复杂的数组声明
int *pr[10]; //指针数组 int &re[10] = /* --*/; //错误,引用错误 int (*pra)[10] = &arr; //指向一个含有10个整数的数组 int (&arre)[10] = arr; //引用一个含有10个整数的数组
理解方法里到外 从右到左 ,先读括号内的,
所以第三个用例中,pra是指针-》它指向一个包含10个元素的int数组
引用相同
int *(&arr)[10] = pr; //arr是个引用,它引用了一个指向包含10个整数的指针int型
5.1 访问数组元素
数组也能使用下标索引,通常定义为
size_t
是一种无符号型的机器相关类型
在头文件
cstddef
里右定义size_t类型
unsigned sc[11] = {}; //11个分段,全被初始化为0
for (auto i : sc) //对于sc
cout << i << " "; //输出计数
cout << endl;
5.2 指针与数组
取地址符可适用与任何对象,数组有个独特的特性直接用到数组的名字,编译器会替换成数组第一个元素的指针
int arr[] = {/*..*/};
string *str = arr; //等价于str = &arr[0];
auto ia(arr); //同上
5.2.1 指针也是迭代器
指向数组的指针拥有更多功能
-
支持运算
int *p = arr; ++p; //指向下一个元素
-
c++11 引入
begin
和end
函数 可相当于迭代器使用了
auto *beg = begin(arr); //beg指向arr第一个元素
sizeof(a)/sizeof(a[0])
知识带入可得到数组维度end(a)-begin(a) 也可以获得
5.2.2 指针运算
解引用、递增、比较、与整数相加减、指针相减。与迭代器一样
-
auto *ip = arr; //指向a[0] auto *ip2 = ip+4; //指向 a[4]
-
不能指向超过数组的元素
- 这个错误编译器发现不了
两个指针相减得到的结果是printff_t得标准库类型,和size_t 一样定义在cstddef头文件中
*
运算级比+,-高所以要加括号才能取运算后的解引用auto a = *(ip+4); //a = arr[4]; auto a = *ip+4; //a = arr[0]+4;
5.2.3 下标和指针
数组名字相当于用指针,所以同样的指向数组中元素的指针,也可以用数组一样的下标使用
auto *p = &ai[2]; //指向为索引为2的元素
auto j = p[1]; //p[1] 等价于*(p+1),就是ai[3]表示的元素
auto k = p[-2]; //就是ai[0];
c++也导入了c的字符串函数风格
#include <cstring>
strlen(p); 返回p的长度,空字符不计算在内
strcmp(p1,p2); 比较大小
strcat(p1, p2); 将p2附加到p1之后,返回p1
strcpy(p1, p2); 将p2拷贝给p1 返p1
使用的字符必须包括空字符
- 以上函数都要注意维度大小不能越界
- 允许用有空字符结束的字符数组来初始化string
- 运算时可以有一个是带有空字符的字符数组
将string 转char的成员函数
string s = "sadasd";
const char *str = s.c_str();
5.2.4 使用数组初始化vector
不允许vector初始化数组,但允许返过来
但需要首尾地址
int arr[] = {0, 1, 2, 3, 4, 5};
vector<int> ive(begin(arr), end(arr)); //所以也可以是一部分
vector<int> ive(arr+1, arr+4); //1-3 3个元素
6 多维数组
int a[3][4];
int a[3][4] = {
{0, 1, 2, 3} 第一行的初始值
{0, 1, 2, 3} 第2
{0, 1, 2, 3} 第3
};
int a[3][4] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3}; //也可以这样
int as[3][4] = {{0}, {1}, {2}}; //初始每行的首元素其他的按默认值初始化为0
int as[3][4] = {0, 3, 4, 5}; //只初始第一行其他默认值为0
int a[10][10][10]... = {0}; 所有元素初始为0
- 运用:
ia[2][3] = arr[0][0][0]; //简单的赋值
int (&row)[4] = ia[1]; //row引用绑定了ia的第二行数组,引用是数组类型的,并不是说他是个引用数组,引用不能是数组
用c++11 的for去赋值
size_t cnt = 0;
for (auto &row : as)
for (auto &col : row) {
col = cnt;
++cnt;
}
使用for除最内层外,其他都应该用引用类型
因为数组的返回元素是指向首元素的指针得到的就是int *型所以无法在int *型中遍历
6.1 类型别名化
using int_array = int[4]; //别名
typedef int int_array[4]; //同上
int ai[3][4];
for (int_array *p = ia; p != ia+3; ++p)
for (int *q = *p; q != *p+4; ++q)
cin << *q;
参考文献:c++ prime 第五版