命名空间的声明
- 头文件中不应该包含using声明
标准库类型string
定义和初始化string对象
- 初始化string对象的方式
string s1; //s1是一个空串
string s2(s1); //s2是s1的副本
string s2 = s1; //等价于s2(s1)
string s3("abc"); //s3是字面值"abc"的副本
string s3 = "abc"; //等价于s3("abc")
string s4(n,'c'); //把s4初始化为有连续n个字符c组成的串
- 使用“=”初始化一个变量,叫做拷贝初始化,相反,如果不使用等号初始化变量,叫做直接初始化。
string对象上的操作
os<<s; //将s写到输出流os当中,返回os
is>>s; //从is中读取字符串赋给s,字符串以空白分隔,返回is
getline(is,s); //从is中读取一行赋给s,返回is
s.empty(); //判断s是否为空
s.size(); //返回s中字符的个数
s[n]; //返回s中第n个字符的引用
s1+s2; //字符串连接,每个加法运算符必须有一个操作数为string类型,不能连接两个字面值
- 读写string操作
- cin>>s; //在读入时,string对象会自动忽略开头的空白,并且遇到空白时(即空格、换行符、制表符等)停止;
- getline(cin,line); //从给定的输入流中读入内容,直到遇到换行符为止,注意和cin的区别
- line.size();//返回一个无符号整型,在line.size()<n这个表达式中,如果n是一个具有负值的int,则n会自动转换成一个比较大的无符号值这样结果肯定为true;所以不要在表达式中混用有符号数和无符号数,尤其是在size中。
- ==:检验两个对象相等
- cin>>s; //在读入时,string对象会自动忽略开头的空白,并且遇到空白时(即空格、换行符、制表符等)停止;
- string::size_type类型
- 对于size函数,其返回值是一个string::size_type类型,是一个无符号类型的值,可以通过auto关键字自动获取返回值类型,eg:auto len = line.size();
- 通过作用域操作符来表明size_type是在string中定义的。
- 如果一条表达式中已经有了size()函数,就不要使用int了,以避免int和unsigned混用可能带来的问题
- 切记,字符串字面值和string是不同的类型
处理string对象中的字符
- 头文件cctype;
- c++的标准头文件为cname格式;
- 处理字符的函数有:判断数字、字母、控制字、字母数字、小写字母、大写字母、大小写字母转化、十六进制数、空白、标点符号、空格等;
- 基于范围的for语句,eg:for(declaration : expression)
- 一般的for语句通过下标,下标定义为:string::size_type n;
- 如果某个索引是带符号整型,则会自动转换成string::size_type类型
- 使用范围for语句改变字符串中的字符
- 注意要改变string对象中字符的值,必须将循环变量定义为引用类型
标准库类型vector
定义和初始化vector对象
- 老版本中,vector< vector<> >内层两边都要有空格,新版本不强调了,但在有的编译器中仍要;
- vector初始化方法
vector<int> v1;
vector<int> v2(v1);等价于v2 = v1;
vector<int> ivec1(10, 5);//ivec包含了10个5
vector<string> svec3(10); // 10个元素,每个都是空的string
vector<string> v1 = {"qwe", "qwer", "qwerty"};//列表初始化只能通过大括号初始化
// 注意花括号和圆括号提供初始化操作的区别
vector<int> v1( 10 ); //v1有10个元素,每个值为0
vector<int> v2{ 10 }; //v2有1个元素,每个值为10
vector<int> v3( 10, 1 ); //v3有10个元素,每个值为1
vector<int> v4{ 10, 1 };//v1有2个元素,一个为10,一个为1
- vector的操作:
- v.empty();
- v.size();
- v.push_back(t);//向v的尾端添加一个元素
- v[n];//返回v中第n个位置上元素的引用
- <,>,<=,=>;//按照字典顺序进行比较
- 如果循环体内包含向vector对象添加元素的语句,那么不能使用范围for循环,即范围for语句体中不应改变其便利序列的大小。
- 不能用下标形式添加元素,但可以访问已经存在的元素;
- 与string类似,定义下标时要:vector<int/string>::size_type n;
迭代器介绍
使用迭代器
- 迭代器的类型都拥有名为begin和end的成员,begin指向第一个元素或者字符,end指向尾元素的下一位置,如果容器为空,则begin和end返回同一个迭代器,都是尾后迭代器。
auto b = ivec2.begin();
auto e = ivec2.end();
- 标准容器的迭代器运算符
- *iter:返回迭代器iter所指的引用,类似于指针
- iter->mem:解引用iter并获取该元素名为mem的成员,等价于(*iter).mem;
- ++iter:令iter指示容器的下一个元素
- 由于end返回的迭代器并不实际指示某个元素,所以不能对其进行递增或者解引用的操作。
- 所有标准容器的迭代器都定义了==和!=,但是他们中的大多数都没有定义<,因此在迭代器的for循环中多用!=;
- 迭代器类型
- 用auto关键字让编译器自动确定,或者用
vector<int>::iterator it;//读写
vectotr<int>::const_iterator it2;//只能读
string::iterator it4;
string::const_iterator it4;//只能读
- const_iterator和常量指针一样,能读但不能修改它所指的元素值,如果vector和string是常量,那么只能使用常量迭代器;
- 如果对象只需读操作而不需写操作的话最好使用常量类型,在c++11中,用cbegin和cend代替begin和end;
auto b = ivec2.cbegin();
- 结合解引用和成员访问操作
- 如果迭代器所指的对象是类,那么可通过解引用和圆点操作符来访问类中的元素。例如,对于一个由字符串组成的vector对象来说,要想检查其元素是否为空,令it是该vector对象的选代器,只需检查it所指字符串是否为空就可以了,其代码如下所示:
(*it).empty() // 注意(*it) . empty () 中的圆括号必不可少。
(*it).mem<==>it->mem:两者等价
(*it).empty()<==>it->empty()
"->"运算符把解引用和成员访问两个操作结合到一起
- 结合解引用和成员访问操作
- !!!!但凡是使用了迭代器的循环体,都不要向迭代器所属的容器中添加元素
迭代器运算
- string和vector迭代器支持的运算
- iter+n;
- 迭代器相减可以得到两个迭代器指针的距离,即右边的迭代器移动多少个距离可以追上左侧的迭代器。得到的距离类型为difference_type,是带符号整型,这个距离可正可负。
- iter1-iter2;
- <,>,<=,>=:判断迭代器的位置关系
- 迭代器的算术运算
数组
- 相对于容器,数组运行性能较好,但灵活性差一点;如果不确定元素的具体个数,请使用vector;
- 数组的声明维度必须为常量表达式,eg:
const unsigned cnt = 42;
constexpr unsigned sz = 42;
- 数组的元素应为对象,不存在引用的数组;数组本身也是对象,允许定义数组的指针及数组的引用。
- 字符数组的特殊性
- 字符数组可以用字符串字面值进行初始化
- 不允许用一个数组初始化另一个数组,也不允许把一个数组直接赋值给另一个数组。
char a3[] = "C++"; //自动添加表示字符串结束的空字符
char a4[6] = "Daniel"; //错误因为没有空间可以存放空字符
- 复杂的数组声明
- 指针和数组:最好的方法是从数组名字开始由内向外的顺序阅读
- []运算符的优先级高于*
int *ptrs[10]; //声明含有10个整型指针的数组;
int(*Parray)[10];//Parray是一个指向大小为10的数组的指针
int &refs[10] = /* ?* /; //错误,不允许使用引用的数组
int(&arrRef)[10] = arr; //arrRef引用一个含有10个整数的数组
- 在C++中指针和数组有非常密切的关系,使用数组的时候编译器一般会把它转化为指针。
- 指针也是迭代器
- c++提供了获取首元素和尾元素指针的函数 :begin()和end()
//标准库函数begin()和end(),定义在iterator头文件中
int ia[] = {0, 1, 2, 3, 4, 5};
int *beg = begin(ia); //指向ia首元素的指针
int *last = end(ia); //指向ia尾元素的指针
C风格字符串
- C风格字符串为将字符串存放在字符数组中并以空字符结束('\0'),eg: char str[] = "c++";
- C风格字符串一般以指针来操作,操作函数为:
- strlen(p);strcmp();strcat(p1,p2);strcpy(p1,p2);
- 传入这些函数的指针必须指向以空字符作为结束的数组;
- 使用标准库string要比使用C风格字符串更安全更高效。
与旧代码的接口
- 允许使用以空字符结束的字符数组来初始化string对象或者为其赋值;
- 反过来,可以用c_str函数实现用string对象初始化指向字符的指针,eg:const char *str = s.c_str();//注意,const必须有
string strr("Hello");
const char *str2 = strr.c_str();//不能改变
//char *str3;
//strcpy(str3, str2);//想要改变需要copy一份
- 允许使用数组来初始化vector对象
int int_arr[] = { 0, 1, 2, 3, 4, 5 };
vector<int> ivec(begin(int_arr), end(int_arr));
- 现代C++ 程序应当尽量使用vector和迭代器,避免使用内置数组和指针,应当尽量使用string,避免使用C风格字符串。
多维数组
- 要使用范围for语句处理多维数组,除了最内层循环外,其他所有循环控制变量都应该为引用类型。
- 指针和多维数组
- 多维数组名实际上是指向第一个内层数组的指针
for (auto pp = begin(ia); pp != end(ia); ++pp)
{
//qq指向内层数组的首元素
for (auto qq = begin(*pp); qq != end(*pp); ++qq)
cout << *qq << endl;
}
//pp为指向ia的第一个内层数组的指针
//然后内层都要用到解引用符获取内层元素
for (auto &row : ia)
{
for (auto col : row)
{
...
}
}