字符串、向量和数组
1. using声明
-
using
声明,注意与using
别名声明相区分1
2using namespace::name; //声明之后可以直接使用命名空间namespace中的name
using std::cin; -
每个名字都要独立的进行
using
声明 -
头文件中不应包含
using
声明。以防不经意间的包含产生意想不到的错误
2. 标准库类型string
-
string
库表示可变长字符序列,使用时首先包含string
头文件。此库定义在std
命名空间中。 -
初始化的方式
-
直接初始化和拷贝初始化
-
拷贝初始化:使用等号初始化
1
string a = "aa";
-
直接初始化:不使用等号初始化
1
2string a("aa");
string b(10,'a');
-
-
相关操作
-
读写操作。读取时,开头的空白会被忽略,从第一个真正的字符开始,到下一处空白结束
1
2
3string s;
cin >> s;
cout << s << endl;返回左侧的运算对象。特别是读取时,如果读取到非法字符(结束符等),则返回流无效,可以作为读取结束的判断标志实现读取未知数量的串。
-
读取一整行。遇到换行符就终止,换行符被读到流中,但是不会存入
string
对象。读取后返回流,同样可作为判断条件。1
2string s;
getline(cin,s); //从cin中读取数据给到s -
判断长度操作
1
2
3
4
5string s;
s.empty(); //判断是否为空,返回布尔值
s.size(); //返回字符个数,即长度。
//size()返回值是string::size_type类型,是无符号整数,注意和有符号数混用时的情况
//如s.size() < n判断条件当n为负值时结果大概率始终为true -
比较操作。逐字符进行比较,且大小写敏感,依照字典顺序。
- 一个是另一个的前缀,则短的小
- 其余则是二者第一个相异字符比较的结果
-
赋值和相加操作
1
2
3
4
5
6
7
8
9
10
11string s1,s2;
s1 = s1; //可直接赋值
string s3 = s1 + s2; //相加结果是串的拼接
string s3 += s1;
//string与字面值相加。字符和串字面值都可以转换为string对象
s3 = s1 + "," + s2 + '\n'; //必须保证每个+两侧至少有一个是string对象
s4 = "a" + 'b'; //错误
//注意
s5 = s1 + "," + 'a'; //正确
s6 = "," + 'a' + s1; //错误
-
-
处理字符,使用
cctype
头文件提供的标准库进行处理,同样是在std
空间中 -
遍历
string
对象-
使用范围
for
语句1
2
3
4
5for(auto c : s) //c的类型为char,每次执行会将s中的下一个字符拷贝给c
cout << c << endl;
//如果想要修改字符,则要使用引用
for(auto &c : s)
c = toupper(c); -
使用下标运算符
[]
,接收参数类型为string::size_type
,从0开始,返回该位置上字符的引用。同样注意带符号类型的转换问题。并且使用时确保对象非空,否则会产生无法预知的结果。1
2if(index!=s.size() && !isspace(s[index])) //注意检测条件的巧妙之处。
//只有索引合法时才会真正的去访问
-
3. 数组
数组用于存放类型相同的对象,且数组大小确定不变。数组和引用、指针类似,是基于其他类型定义的类型,属于复合类型。数组本身也是对象,可以有指向数组的指针和引用。
3.1 定义和初始化数组
数组定义
1 | type a[n]; |
默认初始化
默认情况下进行默认初始化,满足默认初始化的规则
显式初始化
即为列表初始化,int a[n] = {1,2,3,...}
,部分情况会忽略列表维度,有如下几种情况
-
列表定义没有指出列表的维度,则编译器根据初始值的数量计算列表维度
-
列表定义时指出了列表的维度,则初始值总数量不能大于指定的维度
- 初始值数量等于维度,按顺序初始化
- 初始值数量小于维度,使用初始值初始化靠前的元素,剩余元素执行默认初始化
1
2
3
4int a[3] = {1,2,3}; // int a[3] = {1,2,3}
int a[] = {1,2,3}; // int a[3] = {1,2,3}
int a[4] = {1,2,3}; // int a[4] = {1,2,3,0}或者int a[4] = {1,2,3,不确定值}
int a[2] = {1,2,3}; // 错误
字符数组的特殊初始化方式
字符数组除了上述初始化方式之外,还可以使用字符串字面值进行初始化,并且字符串字面值结尾的空字符也会被初始化到字符数组之中
1 | char a[] = "C++"; // char a[4] = {'C','+','+','\0'} |
不允许直接拷贝和赋值
1 | int a[] = {0,1,2}; |
复杂的数组定义
1 | int *a[10]; // 保存指针对象的数组 |
3.2 访问数组元素
数组元素可以使用范围for语句
或者下标运算符
来访问。使用数组下标时,通常将其定义为size_t
类型,size_t
是一种机器相关的无符号类型,定义于cstddef
头文件中
1 | // 范围for语句,a为数组 |
1 | // 下标运算符 |
3.3 指针和数组
数组名字和指针
数组具有一个特点,在用到数组名字的地方,编译器有时会自动将其替换为一个指向数组首元素的指针
1 | string test = {"one","two","three"}; |
使用数组作为一个auto
变量的初始值时,推断得出的类型是指针而不是数组。引用类型除外
1 | int a[] = {1,2,3,4,5,6,7,8,9,0}; |
使用decltype
关键字时不会进行上述转换
1 | int a[] = {1,2,3,4}; |
内置数组的迭代器操作
指向数组元素的指针具有容器类型迭代器的所有功能,并且支持所有迭代器操作和运算。因为迭代器本身就是在容器类型中对指针操作的模仿。
直接获取首元素指针和尾后指针
1 | int a[] = {1,2,3,4,5,6,7,8,9,0}; |
类似容器迭代器,内置数组具有两个函数begin
和end
,将数组作为其参数即可得到首元素和尾后元素指针。
1 | int a[] = {1,2,3,4,5,6}; |
标准库中的begin
和end
只能用于数组类型,接收数组的引用,不能用于指针类型,包括指向数组的指针
1 | int a[] = {1,2,3,4,5,6}; |
指向数组元素的指针支持所有迭代器运算,特别的对于-
运算,得到的结果为ptrdoff_t
类型,是一种机器相关的带符号类型,定义于cstddef
头文件中。
3.4 多维数组
C++语言中没有多维数组,此处所说多维数组实质上为数组的数组
多维数组的定义
1 | int a[3][4]; //大小为3的数组,每个数组是大小为4的数组 |
多维数组的初始化
1 | // 1. 默认初始化,根据默认初始化的规则进行初始化 |
多维数组的下标引用
多维数组的每个维度对应一个下标运算符。如果下标运算符数量等同于维度,则访问结果为数组元素;如果下标运算符数量小于维度,则访问结果为内层数组
1 | int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; |
多维数组的遍历
-
传统
for
语句1
2
3
4
5
6int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(int i = 0;i<3;++i){
for(int j = 0;j<4;++j){
cout << a[i][j];
}
} -
范围
for
语句1
2
3
4
5
6
7
8
9
10
11int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
for(auto &row : a){
for(auto &col : row){
cout << col;
}
}
// 注意此处的row必须为引用类型,以防止数组自动转换为指针
for(auto row : a){ //此处的auto为int*指针类型,下一个范围for语句会报错
....
}
多维数组与指针
多维数组的名字同样有时会被转换为指向第一个子数组的指针。多维数组内部子数组或子多维数组同样符合数组和多维数组与指针的转换关系
1 | int a[3][4]; |
多维数组的指针操作可以视为不同层次的内置数组的指针操作,满足迭代器操作和运算
1 | int a[3][4]; |
多维数组同样具有being
和end
函数,二者返回类型为指针
1 | int a[3][4]; |
多维数组指针的类型别名
可以使用类型别名简化指向多维数组的指针
1 | //新旧两种声明类型别名的方式等价 |