3.1 命名空间的using声明
头文件不应该包含using声明,因为头文件内容会拷贝到引用他的文件中去。
3.2 标准库类型string
3.2.1 定义和初始化string对象
-
初始化string方法
-
直接初始化和拷贝初始化
使用=
的是拷贝初始化,会先创建一个对象,再把这个对象赋值。
3.2.2 string对象上的操作
- 从读入中给string对象赋值。
- 使用标准输入给string赋值。会忽略开头空白(空格、回车、制表符等),从第一个字符开始读取,直到下一处空白。
程序输入“ hello word”,cin>>s
,s中内容是hello。 - 读取未知数量的单词,可以用cin作为读入条件。
- 使用标准输入给string赋值。会忽略开头空白(空格、回车、制表符等),从第一个字符开始读取,直到下一处空白。
int mian(){
string word;
while(cin>>word){//反复读取,直到文件尾
cout<<word<<endl;
}
return 0;
}
- 希望保留输入的空白符,使用
getline()
函数将进行读取。
int mian(){
string line;
while(getline(cin,line)){//反复读取,直到文件尾
cout<<line<<endl;//line中不包含换行符
}
return 0;
}
- s.size()返回类型
- 返回
string::size_type
类型 ,是配套类型。 - 配套类型体现了标准库类型和机器无关的特性。
string::size_type
是无符号类型,可以放下任意string的大小。
- 返回
- string类型相加:
+
的两侧至少有一个是string类,不能两个都是字面值或char*。
3.2.3 处理string对象中的字符
- 建议使用c++中的c标准库头文件。
- 使用范围for语句改变字符串中的字符。
string s{"hello word"};
for(auto &c, s ){
//c是引用,所以才能改变s中的值。
//不声明成引用,在范围for中只是拷贝。
c = toupper(c);
}
- string对象的下标范围在[0,size-1].
- 若string为空,
s[0]
的结果未定义。
3.3标准库类型vector
- vector是容器,也叫类模板。
- 早期c++标准中,声明vector的vector,右尖括号间要有两个空格。某些编译器可能也只能处理这种老式的声明。
vector<vector<int> >.
3.3.1 定义和初始化vector对象
- 列表初始化vector放在花括号内,使用多个相同元素初始化用圆括号。
圆括号提供的值是构造,花括号的值是列表初始化。
vector<int> v1{1,2,3,4} ; //vector中是1~4
vector<int> v2(10,1); //vector中10个1
vector<int> v3(10); //vector中10个0
vector<string> svec{10, "hello"}; //svec中10个"hello".编译器可以接受,但是个人不要使用这种初始化方法!!
3.3.2 向vector中添加元素
v.push_back()
- 范围for中不应该改变遍历序列的大小
3.3.3 其他vector操作
- size()返回类型是size_type类型,要使用的话,也要指明基本类型,如下面的int。当然,使用auto声明更好用。
vector<int>::size_type sz =ivec.size();
- vector 也可以使用下标。但是不能使用下标添加元素。
- 确保下标合法的方法是使用范围for。
3.4 迭代器介绍
3.4.1 使用迭代器
- 试图解引用一个非法迭代器或者尾后迭代器都是未定义的行为。
- end实际上不指向某个元素,所以不能自增或者解引用。
- 比较迭代器一般使用
!=
而不是<
,因为这种风格在标准库提供的所有容器上都有效。 - 声明一个迭代器的方法:
vector<int>::iterator it;//必须说明数据类型
string::iterator it2;
vector<int>::const_iterator it3;//只读型
string::const_iterator it2;
- 获取vector的迭代器的方法
auto b =v.begin();
auto e = v.end();
auto cb = v.cbegin();//类型是vector<xx>::const_iterator
auto ce = v.cend();
- 若迭代器指向一个类,要访问类成员或类函数,
()
必不可少。所以最好用箭头运算符替代,不出错。
(*iter).empty();
iter->empty();//等价于(*iter).empty();
- 不允许在范围for循环中改变vector的容量。
3.4.2 迭代器运算
- 两个迭代器间相减,得到迭代器之间的距离,类型是
difference_type
(带符号型整数) - 迭代器相加没有意义。
- 使用auto方法声明迭代器时,不用声明是const或者引用。若声明成const,
ivec.begin()
返回的是指针,顶层const会使迭代器指针没办法移动,失去了迭代器的作用;若声明成指针,声明出指针的指针,用起来麻烦。
auto it1 = ivec.begin();
const auto it2 = ivec.begin();//it2++这类改变指针指向的操作出错
auto * it3 = ivec.begin();//我的编译器会报错
数组
3.5.1 定义和初始化内置数组
- 定义数组时,必须指定数组类型,不允许用auto推导
- 不存在引用的数组(同vector)
- 字符数组可以用字符串字面值初始化。使用这种方法初始化,数组最后会加上一个空字符。
char c_arr[] ="hi~";//等同于char c_arr[] ={'h','i','~','\0'}
- 数组不允许拷贝和赋值
int a1[],a2[];
a1=a2;//错误,不允许拷贝、赋值
- 复杂的数组声明
类型修饰符从左往右依次绑定
int * ptrs[10];//ptrs含有10个int型指针的数组
int & refs[10]=/*?*/;//错误, refs含有10个int型引用的数组,不存在
int (*parray)[10] =&array;//parray是指针,指向一个10个int的数组
int (&arrRef)[10] = arr;//arrRef是对有10个int的数组的引用。
3.5.2 访问数组元素
- 别越界就ok
3.5.3 指针和数组
- 当数组作为一个auto变量的初始值时,推断出的类型是指针。用decltype返回的仍是数组
int ia[] ={1,2,3};
auto ia2(ia)={1,2,3};//ia2是指针,报错
decltype(ia) ia3 ={4,5,6};//正确
- 迭代器就是指针,迭代器end指向最后一个元素之后。若数组有10个元素,则迭代器end指向a[10]。
- 两指针相减得到的类型
ptrdiff_t
,带符号。与机器类型无关,定义在cstddef头文件中。 - 内置下标运算(如数组)可以是负数,不要求是无符号类型;而标准库类型必须是无符号类型。
int ia[] ={1,2,3};
int *p = &ia[2];
int i = p[-2];//表示ia[0]的元素
3.5.4 c风格字符串
- 以下函数定义在cstring头文件中。
- c风格字符串必须以空字符
\0
结束。
char ca{'c','+','+'};
cout<<strlen(ca)<<endl;//无空字符结尾,严重错误
- 字符串大小的比较必须使用
strcmp
函数,否则只是比较两个char型指针的大小(比较两个互不相关的指针严重错误)。 strcat
用于字符串拼接,strcat(carr1,carr2)
会将carr2中内容拷贝到carr1中。如果carr1中剩余空间不足,放不下拼接内容,会出现严重错误。- string比c风格字符串更安全高效,建议使用string。
3.5.5 与旧代码的接口
- c++中,任何字符串字面值都可以用空字符结尾的字符数组代替。
- 将string转为c风格字符串,使用
c_str
成员函数,返回const char *
。
string *str = s;
const char *str = s.c_str();
- 如果后续操作改变了string的值,
c_str
返回的这个数组会失去作用。安全的使用方法是及时拷贝后使用。 - 可以使用数组初始化vector
int a[]{1,2,3,4,5,6,7,8,9,10};
vector<int> ivec1(begin(a),end(a));
vector<int> ivec2(begin(a)+1,end(a)-1);
多维数组
- 多维数组就是数组的数组
int a[3][4];//有三个元素的数组,每个元素是有4个int的数组。
int arr[10][20][30]={0};//所有元素初始化为0
- 多维数组的初始化
int a1[2][3] ={
{1,2,3},
{4,5,6}
};
int a2[2][3]={1,2,3,4,5,6};//a2与a1等价
int a3[2][3]={{1},{4}};//初始化每行首元素
int a4[2][3]={1,2,3};//初始化第一行,剩下元素进行值初始化(0)
- 多维数组的下标引用
//a1[1]代表a1数组的第二个元素,是一个有4个int的数组,自动转为int指针
//ruo是一个引用,再从左往右看,row是一个对有4个int的数组的引用
int (&row)[4] = a1[1];
- 使用范围for处理多维数组,除了最内层外,其余层都要声明成引用。
若不声明成引用,则数组会自动转成指针,对编译器而言,就是在int**和int*内循环,不合法,不能通过编译。
int a[10][20][30]={0};
int i = 0;
for ( auto &a1 : a)//外层只能是引用
for( auto &a2:a1)
for( auto &a3:a2){//范围for中要修改数据也要声明成引用
a3=i++;
}
for (const auto &a1:a)
for(const auto &a2:a1)
for(const auto a3:a2){//若是只读不写可以不写成引用
cout << a3 << " ";
}
cout << endl;
- 声明指向多维数组的指针
int ia[3][4];
int (*p)[4]=ia;//最基本的方式,注意,小括号必须加上。
for(auto p =ia;p!= ia + 3;p++)//使用auto,ia+3可以用标准库end(ia)代替
for(auto q = *p;q!=*p + 4;q++)
cout<*q<<endl;
using int_array = int[4];
for(int_array *p =ia;p!= ia + 3;p++)//使用类型别名简化声明
for(int *q = *p;q!=*p + 4;q++)
cout<*q<<endl;