3.1 命名空间的using声明
1、格式
using namespace::name;
(1)例:
using namespace::std;
3.2 标准库类型string
1、标准库类型string的定义包含在#include<string>
头文件中。
2、初始化string的方式
(1)直接初始化(不用等号的初始化)
(2)拷贝初始化
(3)例:
string s1;
string s2(s1); //直接初始化,上下等价。
string s2=s1; //拷贝初始化,上下等价。
string s3("value");
string s3="value";
string (n,'c'); //由n个字符c组成的字符串
3.2.2 string的操作
1、string的操作:
1、os<<s; //输出
2、is>>s; //输入
3、getline(is,s); //从is中读取一行赋值给s,返回is
4、s.empty(); //s为返回true,非空返回false
5、s.size(); //返回s中字符的个数
6、s[n] //返回第n个字符引用,n从0计起
7、s1=s2 //用s2的副本替s1中原来的字符
8、s1==s2
//s1和s2所含字符完全一样,则为true,否则为false,区分大小写。
9、s1!=s2 //与s1==s2相反
10、>,<=,>=,< //利用字符在字典中的排序,区分大小写
2、string对象会自动忽略开头的空白(即空格符,换行符,制表符等)
3、读取未知数量的string对象
(1)例:
string word;
while(cin>>word) //反复读取,直至到达文件末尾
cout<<word<<endl;
(2)while语句条件负责在读取时,检测输入流的情况:
如果流有效,即没有遇到文件结束标记或非法输入,就执行while内部的操作。
4、使用getline读取一整行
(1)getline函数的参数是一个输入流和string对象,函数从给定的输入流读入内容,直到遇到换行符。
(2)换行符被读入,但不存入string对象
5、word.size()返回的是无符号值,类型为 string::size_type。
6、比较string对象
(1)比较符号:> ; < ; >= ; <=
(2)比较规范:
<1>如果两个string对象的长度不同,而且较短的string对象的每个字符都与string对象位置上的字符相同,则说较短的string对象小于较长的string对象。
<2>如果两个string对象在某些位置上不一致,则string对象比较的结果其实是string对象中第一对相异字符比较的结果
7、字符串字面值并不是标准库类型string的对象,字符串字面值与string是不同的类型。
3.2.3 处理string对象中的字符
1、使用cctype头文件中的函数(#include<cctype>
)
1、 isalnum(c) //当c是字母或数字时为真
2、 isalpha(c) //当c是字母时为真
3、 iscntrl(c) //当c是控制符时为真
4、 isdigit(c) //当c是数字时为真
5、 isgraph(c) //当c不是空格但可打印时为真
6、 islower(c) //当c是小写字母时为真
7、 isupper(c) //当c是大写字母时为真
8、 isprint(c) //当c是可打印字符时为真
9、 ispunct(c) //当c是标点符号时为真
10、 isspace(c) //当c是空白(空格,横向制表符,纵向制表符,回车符,换行符,进纸符)时为真
11、 isxdigit(c) //当c是十六进制数字时为真
12、tolower(c) //如果c是大写字母,输出对应的小写字母,否则原样输出小写字母。
13、toupper(c) //如果c是小写字母,输出对应的大写字母,否则原样输出大写字母。
2、使用基于范围的for语句处理string对象的每个字符
(1)C++11标准为我们提供了范围for(range for)语句
(2)语法形式:
for(declaration:expression)
statement;
//①expression:是一个对象,用于表示一个序列。
//②declaration:负责定义一个变量,该变量将用于访问序列中的基础元素。
(3)例:
string str("some string");
for(auto c:str)
cout<<c<<endl;
(4)使用范围for语句改变字符串中的字符
①把循环变量定义成引用类型即可
②range for循环的是declaration中的变量,而不是expression的变量。
(5)只处理一部分字符的方法
<1>访问string对象中单个字符的方式
①使用下标运算符[]
②使用迭代器
<2>下标运算符([])接受的输入参数是string::size_type类型的值,返回值是该位置上字符的引用。
<3>例:
string s("some string");
for(decltype(s.size) index=0;
index!=s.size()&&!isspace(s[index]);
++index)
s[index]=toupper(s[index]);
3.2.3 练习
练习3.6:编写一段程序,使用范围for语句将字符串内的所有字符用x代替
string str="abc123";
for(auto &c:str)
c='x';
cout<<str<<endl;
练习3.10:编写一段程序,读入一个包含标点符号的字符串,将标点符号去除后输出字符串剩余的部分
string str;
getline(cin,str);
for (auto c : str)
if (!ispunct(c))
cout << c;
cout << endl;
3.3 标准库类型vector
1、标准库vector表示对象的集合,也称对象的容器(container),其中具有的对象类型都相同。
2、使用vector需要头文件#include
3、vector是一个类模版
4、实例化(instantiation):指编译器根据模版创建类或函数的过程。
5、vector是模版而非类型,由vector生成的类型必须包含vector中元素的类型。
6、引用不是对象,所以不存在包含引用的vector。
7、vector的使用例子,需提供额外的信息是vector内所存放对象的类型:
vector<int> ivec //ivec保存int类型对象
vector<Sales_item> Sales_vec; //保存Sales_item类型对象
vector<vector<string>> file; //该vector的元素是vector对象
3.3.1 定义和初始化vector对象
1、初始化vector对象的方法
1、vector<T> v1; //v1是个空vector,潜在元素为T类型,执行默认初始化。
2、vector<T> v2(v1); //v2包含v1所有元素的副本
3、vector<T> v2=v1; //等价于v2(v1)
4、vector<T> v3(n,val); //v3包含了n个值为val的重复元素。
5、vector<T> v5{a,b,c...}; //v5包含厨师个数的元素,每个元素赋以相应的初始值。
6、vector<T> v5={a,b,c...} //等价于v5{a,b,c...}
2、如果vector对象中的元素类型不支持默认初始化,我们必须提供初始化的元素值。
3、用值初始化和列表初始化vector对象的区别
(1)用()是值初始化,用()提供的值是用来构造(ocnstruct)vector对象的。
(2)用{}提供的值是vector指定类型对象的初值,但类型需一致。
3.3.3 其他vector操作
1、vector支持的操作
1、v.empty(); //如果v不含有任何元素,返回真,否则返回假
2、v.size(); //返回v中元素的个数
3、v.push_back(t); //向v的尾端添加一个值为t的元素
4、v[n]; //返回v中第n个位置上元素的引用
5、v1=v2; //用列表中元素的拷贝替换v1中的元素
6、v1={a,b,c...}; //用列表中元素的拷贝替换v1中的元素
7、v1==v2; //v1和v2相等,元素数量相同且对应位置的元素值都相同为真,否则为假
8、v1!=v2; //与v1==v2相反
9、>;<;>=;<= //以字典顺序进行比较
2、vector.size()返回值类型为vector定义的size_type类型。要使用size_type,需指定它是由那种类型定义的:
vector<int>::size_type; //正确
vector::size_type; //错误
3、vecotr对象(以及string对象)的下标运算符可用与访问已存在的元素,而不能用于添加元素。
4、vector只能对取值已存在的元素执行下标操作
vector<int> ivecl
cout<<ivec[0]<<endl;
//错误,ivec不包含任何元素!!!
3.3.2 节练习
练习3.14:编写一段程序,用cin读入一组整数并将它们存入vector对象:
int a;
vector<int> v1;
while(cin>>a)
v1.push_back(a);
for(auto i:v1)
cout<<i<<endl;
练习3.15:改写3.14,不过这次读入字符串
string str;
vector<string> st;
while (getline(cin, str) && str != "quit")
st.push_back(str);
3.3.3 节练习
练习3.17:编写一段程序,从cin读入一组词并把它们存入一个vector对象,然后设法把所有词都改为大写形式。输出改变后的结果,每个词占一行:
string str;
vector<string> st;
while (getline(cin, str) && str != "quit")
st.push_back(str);
for (decltype(st.size()) i = 0;i < st.size();i++)
{
for (auto c : st[i])
if (islower(c))
cout <<static<char>(toupper(c));
cout << endl;
}
练习3.20:读入一组整数并把它们存入一个vector对象,将每对相邻整数的和输出出来。改写你的程序,这次要求线输出第一个和最后一个元素的和,接着输出第二个和倒数第二个元素的和,以此类推:
(1)
int i;
vector<int> i1;
while (cin >> i)
i1.push_back(i);
for (decltype(i1.size()) j = 0;j < i1.size();j++)
{
cout<<i1[i]+i1[i+1]<<endl;
}
(2)
int i;
vector<int> i1;
while (cin >> i)
i1.push_back(i);
for (decltype(i1.size()) j = 0;j < i1.size();j++)
{
if (j < i1.size() - 1)
cout << "Neighbor number sum :" << i1[j] + i1[j + 1] << endl;
else
cout << "Neighbor number sum :" << i1[j - 1] + i1[j] << endl;
if (j < i1.size()/ 2)
cout << "Head and End number sum£º" << i1[j] + i1[i1.size() - j - 1] << endl;
}
3.4 迭代器介绍
1、string对象不属于容器类型。
2、迭代器(iterator)也是提供对对象的间接访问。
3.4.1 使用迭代器
1、例:
auto b=v.begin(),e=v.end(); //b和e的类型相同
(1)begin成员函数负责返回指向第一个元素的迭代器
(2)end成员函数负责返回指向容器尾元素的下一个位置的迭代器
(3)该迭代器只是的是容器一个本不存在的“尾后(off the end)”
(4)end成员函数返回的迭代器也被称作尾后迭代器(off the end iterator)
2、如果容器为空,begin成员函数和end成员函数返回的是同一个迭代器,都是尾后迭代器。
3、迭代器运算符
(1)==和!=比较两个合法迭代器是否相等
(2)如果两个迭代器指向的元素相同或都是同一个容器的尾后迭代器,则它们相等,否则就说两个迭代器不相等。
(3)标准容器迭代器的运算符
1、 *iter //返回迭代器所指元素的引用
2、 iter->mem //解引用iter并该元素名为mem的成员,等价于(*iter).mem
3、++iter //令iter只是容器中的下一个元素
4、--iter //令iter只是容器中的上一个元素
5、iter1==iter2 //迭代器相等
6、iter1!=iter2 //迭代器不相等
4、因为end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或解引用操作
5、迭代器和迭代器类型
(1)迭代器三种不同的含义
<1>迭代器概念本身
<2>指容器定义的迭代器类型
<3>某个迭代器对象
(2)每个容器类定义一个名为iterator的类型,该类型支持迭代器概念所规定的一套操作。
6、begin和end运算符
(1)begin和end返回的具体类型由对象是否常量决定的
<1>如果是常量,begin和end返回类型为const_interator
<2>不是常量,则返回类型为iterator
(2)便于专门得到const_interator类型返回值,C++11引入两个新函数
<1>cbegin
<2>cend
3.4.2 迭代器运算
1、vector和string迭代器支持的运算
1、iter+n //前移n个元素(前移方向为begin到end的方向)
2、iter-n //后移n个元素
3、iter+=n
4、iter-=n
5、iter1-iter2 //两迭代器的距离(指向同一容器或尾后迭代器),类型为difference_type()
2、迭代器中关系运算符的使用(>、>=、<、<=)
(1)要指向同一个容器或尾后迭代器
(2)关系运算符可以比较迭代器所指位置的前后
例:
it=iv.begin()+1;
mid=iv.begin()+iv.size()/2;
if(it<mid)
....
3.5 数组
1、数组是复合类型
2、数组的声明范例:
int *ptrs[10]; //ptrs是含有10个整型指针数组
int (*ptarray)[10]; //ptarray是一个指向含有10个整型数组的指针
int (& arrRef)[10]=arr; //arrRef引用一个含有10个整型数的数组
3.5.2 访问数组元素
1、数组使用下标[]时,下标类型为size_t(在头文件cstddef定义)。
3.5.3 指针和数组
1、在大多数表达式中,使用数组类型的对象其实是使用一个指向该数组首元素的指针。
2、两个指针相减的结果是一种名为ptrdiff_t的标准库类型(带符号类型)
3、下标和指针
例:
int ia[]={0,2,4,6,8}'
int i=ia[2];
int *p=ia;
i=*(p+2); //等价于i=ia[2]
int *q=&ia[2];
int j=q[1]; //q[1]等价于*(q+1),即ia[3]
int k=q[-2]; //q[-2]等价于*(p-2),即ia[0]
4、内置类型的下标运算符所用的索引值是有符号类型,该点与string和vector不同。
3.5.4 C风格字符串
1、C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种俗成的写法
(1)字符串存放在字符数组中以空字符结束(’\0’)。
2、C标准库String函数(C对应string.h头文件,C++对应cstring头文件)
1、strlen(p); //返回p的长度,不计'\0'。
2、strcmp(p1,p2); //比较p1和p2相等性。p1==p2,返回0;p1>p2,返回正值;p2>p1;返回负值
3、strcat(p1.p2); //将p2附加到p1之后返回p1
4、strcpy(p1,p2); //将p2拷贝给p1,返回p1
3.5.4 节练习
练习3.39:编写一段程序,比较两个string对象。再编写一段程序,比较两个C风格字符串的内容:
string a="abced",b="lkkjilk";
if(a.size()>b.size())
cout<<"string a>b"<<endl;
else
cout<<"string a<b"<<endl;
//C风格字符串
char a[]="abced",b[]="lkkjilk";
if(strlen(a)>stlen(b))
cout<<"string a>b"<<endl;
else
cout<<"string a<b"<<endl;
练习3.40:编写一段程序,定义两个字符数组并用字符串字面值初始化它们,接着再定义一个字符数组存放前两个数组连接的后果。使用strcpy和strcat把前两个数组的内容拷贝到第三个数组中:
char a[] = "kingoliver";
char a2[] = "Archlinux";
char *a1 = strcat(a, a2);
cout <<"strcat(a,a2)="<< a1 << endl;
a1 = strcpy(a, a2);
cout << "strcpy(a,a2)=" << a1 << endl;
3.5.5 与旧代码的接口
1、混用string对象和C风格字符串
(1)允许使用空字符结束的字符数组初始化string对象或为string对象赋值,但反过来不行。
2、string提供了名为c_str的成员函数,可以将string对象转换为C风格字符串
(1)例:
string s=("Hello World");
char *str=s; //错误,不能用string对象初始化字符数组char *
const char *str=s.c_str(); //正确
(2)c_str函数返回值为C风格的字符串,即返回一个指向常量字符数组的指针。
3、使用数组初始化vector对象
(1)不允许使用vector对象初始化数组
(2)可用数组初始化vector对象:只需指定拷贝区域的首元素地址和尾后地址。
(3)例:
int int_arr[]={0,1,2,3,4,5};
vector<int> ivec(begin(int_arr),end(int_arr));
//用于初始化vector对象的值也可能仅是数组的一部分
vector<int> subVec(int_arr+1,int_arr+4);
3.5.5 节练习
练习3.41:编写一段程序,用整型数组初始化一个vector对象:
int a[5] = { 1,2,3,4,5 };
vector<int> arrvec(std::begin(a), std::end(a));
cout << "Array to vector£º";
for (auto i = arrvec.cbegin();i != arrvec.cend();++i)
cout << *i;
cout << endl;
练习3.42:编写一段程序,将含有整数元素的vector对象拷贝给一个整型的数组:
int a;
vector<int> arrv;
while (cin >> a)
arrv.push_back(a);
int arr[5];
cout << "Array number: ";
auto j = arrv.begin();
for (auto i = begin(arr);
i != end(arr) && j != arrv.end();++i, ++j)
{
*i = *j;
cout << *i << " ";
}
cout << endl;
3.6 多维数组
1、多维数组的初始化
(1)理解方法:例
int ia=[3][4]; //大小为3的数组,每个元素为是含有四个整型的数组,3为行,4为列
int ia[10][20][30]; //大小为10的数组,它的每个元素均是大小为0的数组,这些数组的元素是含有30个整型的数组。
自右向左分别为列,行。高。
(2)初始化多维数组的每一行分别用花括号括起来
<1>例:
int ia[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
2、多位数组的下标引用
<1>例:
//ia是大小为3的数组,每个元素都半酣4个整型的数组
//用arr的首元素为ia最后一行的最后一个元素赋值:
int ia[3][4]=a[0][0][0];
int (&row) [4]=ia[1];
//把row绑定到第二个4元素数组上。
3、使用范围for语句处理多维数组
int ia[3][4];
size_t cnt=0;
for(auto &row:ia) //对于外层数组的每一个元素
for(auto &col:row) //对于内层数组的每一个元素
{
col=cnt;
++cnt;
}
4、要使用范围for语句处理多维数组,除了最内层的循环之外,其他所有循环控制变量都应该是引用类型。
5、指针和多维数组
(1)多维数组实际为数组的数组,多维数组的组名为指向第一个内层数组的指针。
<1>例:
int ia[3][4];
int (*p) [4]=ia; //指向含有4个整数的数组,ia为&ia[0]
p=&ia[2]; //p指向ia的尾元素
(2)下属声明,括号不可少
int *ip[4]; //整指针数组,元素为4个
int (*ip)[4]; //指向含有4个整型的数组的指针。
6、类型别名简化多维数组指针
(1)例:
int ia[3][4];
using int_array=int[4];
//等价于上面的声明: typedef int int_array[4];
for(int_array *p=ia;p!=ia+3;++p)
{
for(int *q=*p;q!=*q+4;++q)
cout<<*q<<' ';
cout<<endl;
}
3.6 节练习
练习3.43:编写3个不同版本的程序,令其均能输出ia的元素。版本1使用范围for语句管理迭代过程;版本2和版本3都是用普通的for语句,其中版本2要求使用下标运算符,版本3要求用指针。此外,在所有3个版本的程序中都要直接写出数据类型,而不能使用类型别名、auto关键字或decltype关键字:
int ia[3][4];
size_t cnt = 0;
int j = 0;
for (auto & row : ia)
{
int i = 0;
for (auto &col : row)
{
col = cnt;
++cnt;
cout<<"ia["<<j<<"]"<<"["<<i<<"]="
<<col<<" ";
++i;
}
++j;
cout << endl;
}
//初始化二维数组
版本1:
for (int(&p)[4] : ia)
{
for (const int(&q) : p)
cout << q << ' ';
cout << endl;
}
版本2:
for (size_t i = 0;i < 3;++i)
{
for (size_t j = 0;j < 4;++j)
cout << ia[i][j] << ' ';
cout << endl;
}
版本3:
for (int(*p)[4] = ia;p != end(ia);++p)
{
for (int *q = *p;q != end(*p);++q)
cout << *q << ' ';
cout << endl;
}
术语表总结
1、缓冲区溢出(buffer over flow):一种严重的程序故障,主要原因是视图通过一个越界的索引访问容器内容。
2、容器(container)是一种类型,其对象容纳了一组给定类型的对象。vector是一种容器类型。
3、difference_type:由string和vector定义的一种带符号整数类型,表示迭代器之间的距离。
4、实例化(instantiation):编译器生成一个指定的模版类或函数的过程。
5、ptr_diff_t:是cstddef头文件中定义的一种与机器实现有关的带符号整数类型,它的空间足够大,能够表示数组中任意两个指针之间的距离。
6、size_t:是cstddef头文件中定义的一种与极其实现有关的无符号整数类型,它的空间足够大,能够表示任意数组的大小
7、size_type:是string和vector定义的类型的名字,能存放任意string对象或vector对象的大小。在标准库中,size_type被定义为无符号类型。