第三章 字符串、向量和数组
c++11
- auto或decltype缩写string::size_type -- 3.2.2
- 范围for循环 -- 3.2.3
- vector的vector声明语句 -- 3.3
- 列表初始化 -- 3.3.1
- cbegin与cend -- 3.4.1
- 数组的begin与end -- 3.4.2
- 多维数组的auto或decltype的for范围 -- 3.6
第三章 字符串、向量和数组
第二章的数据类型是内置数据类型,直接由硬件实现;c++标准库实现了高级数据类型,并未硬件直接实现。vector
与string
是由标准库实现的,而数组是内置类型,但是不够灵活。
3.1 命名空间的using声明
- using
using std::cin;
- 每个using语句只能声明名字空间的一个成员
- 头文件不应该使用using声明,因为可能引起include该头文件的文件的命名冲突
3.2 标准库类型string
#include<string>
using std::string;
3.2.1 定义和初始化string对象
string s1;//默认初始化为空字符串
string s2(s1);//直接初始化,s2是s1的副本
string s2=s1;//拷贝初始化,s2是s1的副本
string s3("value");//直接初始化,s3是“value”的副本,不包含最后的空字符
string s3 = "value";//拷贝初始化,s3是“value”的副本,不包含最后的空字符
string s4(10,'c');//直接初始化,s4是10个连续的‘c’,第二个参数只能是char不能是string
string s4 = string(10,'c');//先创建一个临时变量,然后定义s4并初始化为该变量的副本,如下。相比直接初始化,并没有优势。
string temp(10,'c');
string s4 = temp;
3.2.2 string对象上的操作
- 读写
int main()
{
string s;
cin >> s;//遇到空格停止
cout << s <<endl;
return 0;
}
- 读取未知数量的string对象
int main()
{
string s;
while(cin>>s)
cout<<s<<endl;
return 0;
}
- 使用
getline
读取一整行:为了读入空格使用getline,遇到换行符停止,换行符也会被读取但是不保存(被丢弃),如果一开始就是换行符则返回空string
int main()
{
string line;
while(getline(cin, line))
cout<<line<<endl;
return 0;
}
- string的
empty
与size
操作
while(getline(cin,line))
if(!line.empty())
cout<<line<<endl;
while(getline(cin,line))
if(line.size()>80)
cout<<line<<endl;
- string::size_type
size()函数返回的是一个无符号整数,但是数据类型是string内定义的string::size_type;
因为是无符号整数,所以表达式中有int会转成无符号整数,这会使得负值变成更大的值,应该尽量避免。
可以使用auto或者decltype来获取string::size_type类型
auto len = line.size();
- 比较string对象
(1) 长度不同但短对象对长对象对应相同,则短对象小
(2) 按照第一个相异字符比较大小 - 赋值
string x(10,'c'),y;
x = y;
- string相加
string s1 ="abc", s22="def";
string s3 = s1 + s2;
s1 += s2;
- 字面值与string相加
字符字面值或字符串字面值与string相加时可以自动转换成string类型,相加结果也是一个string;
要保证加号+
两边至少有一个string,即字面值之间不能想加;
当有多个加号时,按照加号运算顺序执行,如果可加则计算结果也是string,那么后面执行的加号一侧的数据类型是相加后的string而非原来的类型;
字符串字面值不是string。
string s1="aa";
string s2 = "m"+"n"+s1;//错误,与下面等价
string temp = "m"+"n";
string s2 = temp + s1;
string s2 = s1+"m"+"n";//正确,与下面等价
string temp = s1+"m";
string s2 = temp + "n";
3.2.3 处理string对象中的字符
- 在头文件cctype中定义了字符操作函数
isalnum(c)
isalpha(c)
iscntrl(c)
isdigit(c)
isgraph(c)
islower(c)
isprint(c)
ispunct(c)
isspace(c)
isupper(c)
isxdigit(c)
tolower(c)
toupper(c)
- c++标准库cname兼容了c标准库name.h,并且cname中的名字在std命名空间中,而name.h不在其中。
(1) 范围for循环
- 范围for循环
string str("abcd");
for (auto c : str)
cout << c << endl;
- 引用
string str("abcd");
for (auto &c : str)
c = toupper(c);
cout << str << endl;
(2) 下标
- 下标运算符
[]
,参数是string::size_type类型,返回值是字符引用 - 从0开始,到s.size()-1;如果超出范围会有问题,因此对于空string,不能使用索引;
if(!s.empty()) //检测是否有第一个字符
cout << s[0] <<endl;
- 任何表达式只要是整型值就能做索引,有符号数会自动转换为无符号数;
- 使用下标迭代
for(decltype(s.size()) index=0; index!=s.size() && !isspace(s[index]); index++)
s[index] =toupper(s[index]);
将下标定义为string::size_type可以保证大于等于0,只要再限制小于s.size()就可以了;
在for循环的条件中,先验证下标范围,再验证通过下标访问的其他条件,可以保证后者不会出现越界问题。
3.3 标准库类型vector
#include<vector>
using std::vector;
- vector是类型模板
vector<int> ivec;
- vector能容纳大多数类型,但是引用不是对象,没有包含引用的vector。
- 在c++11之前,vector的元素是vector时,其定义需要在两个
>
之间加一个空格,c++11不需要。
vector<vector<int> > ivv;//c++11之前
vector<vector<int>> ivv; //c++11
3.3.1 定义和初始化vector对象
vector<T> v1;
vector<T> v2(v1);
vector<T> v2 = v1;
vector<T> v3(n, val);
vector<T> v4(n);
vector<T> v5{a,b,c...}
vector<T> v5 = {a,b,c...}
(1) 列表初始化
vector<T> v5 = {a,b,c...};
vector<T> v5{a,b,c...};
- 拷贝初始化(=)只有一个初始值
- 类内初始值只能用拷贝初始化与列表初始化
- 提供初始化元素列表,只能用列表初始化,不能用圆括号
(2) 创建指定数量元素
vector<T> v3(n, val);
(3) 值初始化
vector<T> v4(n);
- 如果没有指定元素初值,则默认初始化。如果是类,则按照类默认初始化。
- 有些类要求提供初始值,不能默认初始化,必须提供元素值。
(4) 列表初始值或元素数量
- 圆括号只能用于构造,指定数量或者初始值
- 花括号既可以列表初始化,也可以替代圆括号,优先检测花括号内部的值是否是元素类型,即能否列表初始化,如果不能,若符合圆括号内部的参数形式,则执行构造。
3.3.2 向vector中添加元素
- push_back
vector<int> v2;
for(int i=0;i<100;i++) v2.push_back(i);
- 实时读取
string word;
vector<string> text;
while(cin>>word) text.push_back(word);
3.3.3 其他vector操作
v.empty()
v.size()
v.push_back()
v[n]
v1 = v2
v1 = {a, b, c...}
v1 == v2
v1 != v2
<, <=, >, >=
- 比较操作与string一样,但是对于无法比较的元素不能进行比较
- empty和size与string一样,size的返回值类型也是size_type,但是对于vector而言size_type是与元素类型相关的。
vector<int>::size_type //正确
vector::size_type //错误
- 不能用下标添加元素,只能访问已有元素
3.4 迭代器
3.4.1 迭代器
auto a = v.begin(), e = v.end();
- .begin()指向第一个元素,.end()指向尾后元素,是尾后迭代器。当容易为空,begin与end都是尾后迭代器。
- 使用auto可以不需要知道迭代器类型。
(1) 迭代器运算符
*iter //不能解引用非法或者尾后迭代器
iter->mem //即(*iter).mem
++iter
--iter
iter1==iter2
iter1!=iter2
(2) 移动迭代器
for (auto it =a.begin(); it!=a.end(); ++it)
*it=toupper(*it);
- 不能修改尾后迭代器,只是一个标志。
(3) 迭代器类型
vector<int>::iterator it;
vector<int>::const_iterator it;
- 常量容器只能用const_iterator,否则都可以用。
- const_iterator只读不写,否则可读可写。
(4) begin和end运算符
- 默认非常量容器返回iterator,常量容器返回const_iterator。
- 有时候希望非常量容器返回const_iterator,不做修改,则使用cbegin与cend。
(5) 结合解引用与成员访问
- 解引用与成员访问有先后问题:
*it.empty();
(*it).empty();
- 为了避免混淆,定义箭头运算符
it->empty();
3.4.2 迭代器运算
vector或string支持的运算
it+n
it-n
it+=n
it-=n
it1-it2
>,<,>=,<=
- 迭代器相减的结果是difference_type类型,是有符号整数。
3.5 数组
- 数组大小不变
3.5.1 定义和初始化数组
- 要求编译时维度已知,即唯独必须是常量表达式
int i=10;
string s[i]; //错误
string s[get_size()]; //get_size的返回值必须是constexpr
- 定义数组时必须明确指明类型,不能适用auto
- 数组元素必须是对象,不能是引用
(1) 显示初始化数组元素
- 在初始化时没有指明维度,编译器自行推断
- 如果指明维度,不能小于初始化列表长度
- 如果维度大于初始化列表长度,优先初始化前面的元素,后面的元素默认初始化
(2) 字符数组的特殊性
- 使用字符串字面值初始化字符数组时,会包含结束的空字符
(3) 不允许拷贝和赋值
- 不能使用其他数组来拷贝初始值,也不能赋值。
(4) 复杂声明
int *it[10]; //int*数组
int &it[10]; //int&数组,不存在
int (*it) [10]; //先从内向外看,it是一个指针,然后从右向左看,指针指向维度为10的数组,数组元素类型是int
int (&it) [10]; //先从内向外看,it是一个引用,然后从右向左看,引用维度为10的数组,数组元素类型是int
int *(&it) [10]; //先从内向外看,it是一个引用,然后从右向左看,引用维度为10的数组,数组元素类型是int*
3.5.2 访问数组元素
- 范围for循环或者下标
- 数组下标是size_t类型,定义在cstddef中。
3.5.3 指针和数组
- 数组元素是对象,可以取地址
string *p=&s[0];
- 数组名字默认看作第一个元素的地址
string *p=s;
auto p(s);//p是string *类型
- decltype会将数组名字的类型提做相同类型(元素类型+大小)的数组
decltype(ia) ia2={1,2,3};
(1) 指针是迭代器
int *p=arr; //第一个元素
int *e=arr[10]; //arr尺寸是10,指向尾后元素
(2) begin和end
int *beg = begin(ia);
int *last = end(ia);
- 定义在iterator中。
(3) 指针运算
- 迭代器的运算,指针同样适用
- 指针相减的结果是ptrdiff_t类型,定义在cstddef中,是有符号整数。
- 指针比较必须要求指向同一个对象或者对象后下一个位置
- 指针运算同样适用于空指针和非数组对象
(4) 解引用和指针运算的交互
int last = *(it+4);//先指针运算,再解引用
int last = *it+4; //先解引用,再指针运算
(5) 下标和指针
- 数组下标运算实则是指针运算
int i =a[10];//等价于下面
int *p =a;
int i =*(p+10);
- 相对的,指针运算可使用下标运算实现,只要该指针指向数组中的元素或者下一个位置
int *p =&a[10];
int j = p[3]; //即*(p+3)
int k = p[-3];//即*(p-3)
- 数组作为内置类型,其下标可以是负值,而标准库类型vector或string只能是无符号整形。
3.5.4 C风格字符串
(1) C标准库string函数
strlen(p) //返回长度,包括空字符
strcmp(p1,p2) //p1>p2正值,反之负值,相等0
strcat(p1,p2) //拼接,返回p1
strcpy(p1,p2) //拷贝,返回p1
- p必须是以空字符结尾的字符数组指针(数组名)。
(2) 比较字符串
- 不同于string可以使用关系运算符,C风格字符串使用关系运算符实则是比较两个字符串首地址指针,自然是不能比较的
(3) 字符串大小
*不同于string使用+做拼接,C风格字符串是在做指针相加,没有意义;strcpy与strcat要求目标字符数组有足够大的空间。
3.5.5 与旧代码的接口
(1) 混用string对象和C风格字符串
- 对于string而言,任何字符串字面值出现的地方都可以使用以空字符结尾的字符串数组
- 为string初始化或者赋值
- string加法可以使用最多一个空字符结尾的字符串数组;
- 反之不然,但是可以使用.c_str()将string返回为C风格字符串,数组内容与string一样
char *p=s;//错,s是string,不是char*
char *p=s.c_str();//正确
(2) 使用数组初始化vector
- 不能使用数组或者vector初始化数组,但是反之可以
vector<int> ivec(begin(arr),end(arr));
vector<int> ivec(arr+2,arr+5);
3.6 多维数组
- 多维数组是数组的数组
3.6.1 多维数组的初始化
int ia[2][2]={
{1,2}.
{3,4}
};
int ia[2][2]={1,2,3,4};
int ia[2][2]={{0},{1}};//第一列
int ia[2][2]={0,1};//第一行
3.6.2 多维数组的下标引用
- 如果下标维度一致,则引用元素
- 如果下标维度更小,则引用对应的数组
3.6.3 范围for循环
- 外层(数组数组层)必须用引用,否则auto类型是指针,指针不能进行内层循环
- 最内层如果要修改可以使用引用,也可以不用
- 如果不修改,外层可以用常量引用
for (auto &row:ia)
for (auto &col:row)
for (const auto &row:ia)
for (auto col:row)
3.6.3 指针和多维数组
- 指针必须是对应内层数组的数据类型,其初始化或赋值使用外层数组名则对应第一个内层数组,或者使用明确的内层数组地址时对应内层数组,其解引用是对应的内层数组;内层数组的指针的解引用是内层数组名,即第一个元素的地址指针
int ia[3][4];
int (*p) [4]=ia;
p = &ia[0];
- 应用auto或者decltype
for(auto p=ia;p!=ia+3;++p)
for(auto q=*p;q!=*q+4;++q)
cout<<*q<<" ";
- 可以使用begin与end
for(auto p=begin(ia);p!=end(ia);++p)
for(auto q=begin(*p);q!=end(*q);++q)
cout<<*q<<" ";
3.6.4 类型别名简化多维数组的指针
using ia=int[4];
typedef int ia[4];
for (ia *p;p!=ia+3;p++)
fpr(int *q=*p;q<*p+4;q++)
本文来自博客园,作者:ETHERovo,转载请注明原文链接:https://www.cnblogs.com/etherovo/p/17335117.html