C++ primer 第三章 字符串 向量 数组
1. string(可变长字符串)
1.1 字符串定义与初始化
初始化的几种形式:
(1)拷贝初始化: =
(2)直接初始化:()
(3)列表初始化: {}
注意:初始化时 (1)等号可以用于直接初始化与列表初始化
(2)用字符串字面值初始化时,不会将其中’\0’拷贝进去。
1.2 字符串对象的操作
os<<s;//返回os
is>>s;//返回is(忽略开头空白(即空格符,换行符,制表符))
getline(is,s);//从is中读取一行给s(一行即is中遇到换行符为止,不忽略开头的空白符,除换行符外其余空白符均赋给对象s),返回is
s.empty();
s.size();
s1+s2//返回连接结果
s+字符字面值or字符串字面值//字面值将自动转换为字符串对象。注意:等号两边至少有一个字符串对象。
s1==s2;
s1!=S2;
< <= > >=;
1.3 如何处理字符串中的字符
1.3.1 处理与改变字符
1.范围for语句加上处理(改变)语句,例如cctype文件中的一些函数。
string str("somr string");
for(auto c: str)
{
}//每次迭代,str下一个字符会拷贝赋值给c;故要想改变字符值,需将c定义为引用。
注意:范围for语句中不能为序列添加或删除元素(字符),因为范围for语句为序列预留了end()的值,一旦添加删除,end()值就可能无效(即不表示任何元素)。
2.使用下表运算符[]。
注意:
(1)[]接收参数是string::size_type类型的值,返回的是一个引用。
(2) 在使用[]之前,需要检验那个位置的确有值。
3.使用迭代器
迭代器是一种数据类型,并且只有它这种类型能执行某套操作(寻找容器(string)对象的元素,能从一个元素(字符)移动到另一个元素(字符))
2. vector (可变长对象集合)
2.1 vector是一个类模板
如何给一个类模板提供信息来实例化类?
通过在模板名字后跟一对尖括号,尖括号内放上信息。
如何给一个函数模板提供信息来实例化函数?
通过实参。
2.2 定义和初始化vector对象
初始化的几种形式:
(1)拷贝初始化: =
(2)直接初始化:()
(3)列表初始化: {}
(4)借助数组与迭代器初始化
== 等号可以用于直接初始化与列表初始化==
注意:初始化时有三种例外:
- 类内初始化不能用(),会造成函数声明的歧义;
- 列表元素值的初始化只能用{},而不能用();
因为初始化的真实含义因为()与{ }的不同而不同,前者提供的值用来construct(构造)vector 对象;后者的值为我们列表化初始值,会尽可能把{ }中的值当成元素初始值进行初始 化。
vector<int> a{10};//表示a有一个元素,该元素值为10
vector<int> a{10,20};//表示a有两个元素,元素值为10,20
vector<int> a(10);//表示a有10个元素,每个都是默认初始化值0
vector<int> a(10,20);//表示a有10个元素,每个元素值都为20
在无法用{ }中值执行列表初始化时编译器才会考虑用这些值构造vector对象。
vector<string> a{10,"kobe"};//显然不能用int值来初始化string,所以尽管是花括号,但却是被编译器用来构造vector对象a。
- 使用=时,只能提供一个初始值;
然而实际上c++标准要求vector能在代码运行时高效快速的添加元素,因此除非vector 对象所有元素值都一样时用上述初始化方法,否则更有效的方法是:先定义一个空的vector,再在运行时向它添加具体值(如利用vector成员函数 push_back() )。
2.3 vector操作
a.empty()
a.size()//返回值为 vector<Type>::size_type,不是vector::size_type
a.push_back()//不能在范围for循环中为vector对象添加元素。
a[n]//返回值为a中第n个元素的引用,n为size_type型,同样是vector<Type>::size_type,不是vector::size_type;且a不是空vector,即下表运算符不能用来添加元素,只能改变元素。
a1==a2 //两者元素数量相同且值一样,前提是只有vector中的元素定义了比较操作
a1!=a2
<,<=,>,>=//以字典顺序比较
3.数组 (固定变量集合)
数组与C语言中一致,只需要注意:
1. auto 关键字不能用于数组来推断数组元素类型。
2. 数组的元素为对象,故不存在引用的集合。
3. 要理解数组声明的含义,从数组名开始,由里及外,根据优先级顺序阅读.
例如: int *(&array)[10] 表示array是一个引用,该引用对象是一个数组,数组元素是int型指针.
4.不同于容器,数组的下表运算符[]可以接收负数 ,例如a[-2]表示指向a[0]前面的第二个元素。
int ia[]={0,1,2,3,4};
int *p=&ia[2];
int k=p[-2];//p[-2]是ia[0]表示的那个数。
但是不建议这样书写,因为a[-1]的地址是不确定的, 可能会影响到系统运行。如果同上面一题一样加上指针,可以巧妙地解决某些问题。
5.使用范围for处理多维数组时,除了最内层的循环变量外,其余循环的控制变量都要声明为引用类型。
constexpr size_t row = 2, col = 3;
int array[row][col] = { 1,2,3,4,5,6 };
for ( const auto &a : array)//如果不声明为引用,a变成int*型,则在第二层循环中遍历指针了。
for (const auto b : a)
cout << b<<' ';
或者改用decltype声明。
constexpr size_t row = 2, col = 3;
int array[row][col] = { 1,2,3,4,5,6 };
for ( decltype(array[0]) a : array)//decltype将array[0]看作含有四个整型数据的数组类型
for (auto b : a)
cout << b<<' ';