《C++ Primer 第五版》阅读过程查漏补缺 Chapter3

这一章名为字符串、向量和数组,主要区别于C和java的个人感觉就是向量这个概念。

命名空间Using的使用

  1. 通过using namespace::name;进行声明的方式也可以将某个指定的函数导入命名空间,后面使用无须专门的前缀。
  2. 一般在头文件中不会使用using声明。

标准库类型:string

string类型是定义在命名空间std中的。

初始化方法

C++有多种初始化方式。在这里总结为拷贝初始化和直接初始化。

  1. 拷贝初始化:使用 ”=“ 号进行初始化变量,实质进行了拷贝初始化
    eg. string s1 = string("value),string s2 = "abcde"string s3 = string(10,'c')(表示s3初始化为了由10个c组成的字符串)
  2. 直接初始化:否则就是直接初始化
    eg. string s4(s1)string s5("jlkad")或者string s6(10,'c')

string对象的操作

  1. getline函数需要注意的点: getline(is,s) 从输入流is中读取一行赋值给s,但是不包括换行符。
  2. size()函数需要注意的点 string::size_type类型是size()函数所返回的类型。虽然大多数时候我们在编程时习惯用int型变量来承载string的size()函数的返回值,但事实上,他真正返回的是一个无符号的整数。所以如果表达式混杂了有符号和无符号数,就可能出现这个问题。本书后面还提到了一个关键字decltype()用来得到某个函数size()或者变量的类型。
  3. 比较string对象需要注意的点:比较顺序:先比较每个位置的字典序,然后再比较长度。
  4. string对象的加法:必须保证‘+’号左右的运算对象至少有一个是string类型。字面值是无法和字面值相加的。

string对象中的字符操作

关于C++版本的C标准库头文件

C++标准库兼容了C语言的标准库。C语言中形如name.h的头文件,在C++中被命名为cname。
例如cctype头文件和C语言中的ctype.h头文件的内容是一样的。一般而言,C++程序应该使用cname的头文件。在cname头文件中定义的名字都从属于std命名空间。下表是cctype头文件中比较常见的字符判断操作函数。
在这里插入图片描述

基于范围的for语句

遍历给定序列的每个元素并进行操作,c++11标准给出了新语句,语法形式形如:

1
2
for (declaration  : expression )
statement

其中expressio表示序列的一个对象,declaration部分定义了一个变量,用来遍历序列中的每个元素。

1
2
3
// eg 获得字符串中的每个字符进行操作变为大写
for (auto &c : s)
c = toupper(c); //c是引用,所以可以改变

标准库类型:vector

  1. 事实上来讲c++中的vector其本质是一个类模板。尽管他被称为对对象的集合。
    在C++中,编译器根据模板创建类或函数的过程称为实例化。对于像vector这样的类模板来说,我们通过提供一些额外信息来指定模板到底实例化为什么样的类,具体提供什么信息由模板决定。提供信息的方式是这样的:在模板名字后面跟上一对尖括号,在括号中放上信息
  2. vector可以容纳绝大多数类型的对象作为元素,除了引用,因为引用不是对象。其他绝大多数(非引用)内置类型和类类型都可以构成vector对象,甚至组成vector的对象本身也能是对象。
    注:早期版本C++标准如果vector的元素是其他模板类型,必须在外层vector对象的右尖括号和其元素类型之间添加一个空格。如vector\>在早期版本应该为vector\ >.
  3. vector的初始化有很多种。类似于string,但是需要注意的是,尽管我们在很多时候使用vector来作为动态数组进行使用,但是我们不能像在数组初始化时直接使用{}来进行整体的初始化。vector在使用{}进行初始化时,只有一种用法就是将vecotr包含大括号内值个数的元素,然后每个元素被赋予相应初始值。当然更一般的用法是直接创建一个空的vector对象,后面需要时逐一添加。
  4. 注意在拷贝初始化时,两个vector对象的类型必须相同。
  5. 初始化时区分是列表初始化还是元素数量主要依赖于传递初始值时使用花括号还是圆括号。但是特殊情况是如果使用了{}但是{}中的元素却和vector的类型不相符,则可以考虑他是否是初始化元素数量。例如:
1
2
vector\<string\> v7{10};		// v7有10个默认初始化的元素
vector\<string\> v8{10,"hi"}; // v8有10个值为“hi”的元素
  1. 向vector对象中添加元素一般使用的是push_back()函数,该函数会向vector对象末尾添加元素。注意对vector对象添加元素时蕴含有编程假定:
    a. 必须要确保所写的循环正确无误,特别是在循环有可能改变vector对象容量的时候。
    b. 如果循环体内包含有向vector对象添加元素的语句,则不能使用范围for循环。
  2. vector不能使用下标形式来添加元素,对于vector的下标运算符,只能对确定已存在的元素进行使用。

迭代器

所有的标准库容器都可以使用迭代器。迭代器类似指针类型,提供了对对象的间接访问。
迭代器分有效和无效,有效迭代器指向某个元素,或者容器中尾元素的下一位置。
可以使用迭代器的类型(容器和string)都拥有beginend成员。其中begin自然指向容器或者string的第一个元素或字符,而end则指向容器或者string的“尾元素的下一位置”。所以end返回的迭代器常被称为尾后迭代器如果容器为空,则begin和end返回的是同一个迭代器,都是尾后迭代器·
迭代器有一些自己的运算符。
迭代器运算符
我们必须使用解引用符来获取迭代器所指向的元素,该迭代器必须合法并确实指向某个元素。例如尾后迭代器就无法被解引用。
循环时可以用auto定义的变量来承接begin所返回的迭代器。然后进行++操作。同时使用判断it是否等于end来判断循环是否结束。_注:这里c++中一般使用!=来判断是否遍历完成而不是<来判断,是因为所有的标准库容器的迭代器都定义了!=和==,但不是所有的都定义<运算符,所以一般习惯性使用迭代器和!=进行配合。
迭代器的精确类型一般我们并不关心,拥有迭代器的标准库类型使用iteratorconst_iterator来表达迭代器类型。形如:vector\::iterator it。对于begin和end运算符,返回的具体类型由对象是否是常量决定。分别返回const_iterator或者iterator。C++11提供了两个新的函数cbegin和cend,他们返回的都是const_iterator。
另外由于vector的动态增长性,可能会使其迭代器失效。
迭代器可以进行一部分运算,比如我们可以通过auto mid = (v.begin()+v.end())/2 得到一个指向v容器中间的元素的迭代器mid,并利用比较操作符来控制遍历。

数组

数组和vector十分类似,但是大小确定不变。定义时必须使用常量表达式进行声明数组的维度(元素个数),默认情况下,数组的元素被默认初始化。不允许直接将数组内容拷贝给其他数组作为初始值,也不能直接用数组给其他数组赋值。(但是某些编译器可能支持,因为可能存在编译器扩展,但是标准特性是这样的)
复杂的数组声明一般要从内向外读,先读括号内的内容:

1
2
int (*Parray)[10] = &arr;		// Parray 指向一个含有十个整数的数组
int (&arrRef)[10] = arr; // arrRef引用一个含有十个整数的数组

指针和数组

和C语言中的类似,不过在C++11中提供了标准库函数begin(array)和end(array)。

C风格字符串

cstring头文件中包含了strlen(p)、strcmp(p1,p2)、strcat(p1,p2)、strcpy(p1,p2)这些函数。传入这些函数的指针,必须指向以空字符’\0’作为结束的数组。

与旧代码的接口

很多C++程序再标准库之前就写出来了,所以对于vector和string类型,有一些c++程序使用的是C语言或其它语言的接口程序。所以C++提供一些工具来和这些代码衔接。
在混用string和C风格字符串上,C++提供了下述手段:
从string方向来讲:

  1. 允许使用以空字符结束的字符数组初始化string对象或为其赋值
  2. 在string对象的加法运算中允许使用以空字符结束的字符数组作为其一运算对象
    但是反过来讲,为了将string类型字符串也应用于字符数组,string提供了c_str的成员函数。