第三章 字符串、向量和数组

版权声明:转载请注明出处 https://blog.csdn.net/weixin_39918693/article/details/86563981


一、命名空间的using声明

标准库中的名字都属于命名空间std,我们可以通过“std::名字”来直接使用标准库中的名字。也可以使用using namespace::name; 来进行针对性的暴露操作,就可以在后面的程序中直接使用该名字。每个名字都需要独立的using声明。头文件不应包含using声明。

网络上的很多例子直接对整个std进行了曝光,这是非常不合理的。这就使得命名空间失去了其原本的作用


二、标准库类型string

C++标准对库类型所提供的操作做了详细的规定,而且也对库的实现者作出了一些性能上的需求。所以一般来说,标准库的性能足够强大

如何初始化类的对象是由类本身决定的。一个类可以定义很多种初始化对象的方式(构造函数的重载)

string对象中不包含‘\0’字符(即使是用字符串字面值常量来初始化的string对象也没有‘\0’字符)。‘\0’是C风格字符串所特有的。“字符串字面值常量”是C风格字符串

拷贝初始化和直接初始化(等号和括号)

类既能定义通过函数名调用的操作也能定义各种内置运算符在该类对象上的新含义(运算符重定向)

使用‘>>’运算符从标准输入中读取数据时,以空白(空格符、换行符、制表符等)分隔

注意getline函数的使用,该函数读取的那一行数据中不包括换行符(换行符被丢弃)

string对象的比较是按照字典中的顺序进行的

大多数标准库类型都定义了几种配套的类型(::size_type),这些配套类型体现了标准库类型与机器无关的特性,string::size_type是一种无符号整数(注意无符号数与有符号数不能混用,有符号数向无符号数转换的限制)

标准库允许把“字符字面值常量”和“字符串字面值常量”转换成string对象,在语句中要保证每个运算符(+)的两侧最少要有一个string对象

其中调用了string的构造函数,隐式调用的*

C++中的“字符串字面值常量”并不是string对象,其是C风格字符串——带有‘\0’的字符常量数组(为了与C兼容)

*字符常量数组

cctype头文件中定义了很多处理字符的标准库函数

注意:在C++编程中,如果需要使用C标准库头文件,尽量使用C++版本的C标准库头文件,虽然内容一样,但是其命名更符合C++的语言要求。而且C++版本的C标准库头文件中的名字都定义在std命名空间中。使用简单

范围for语句:遍历给定序列中的每个元素并对序列中的每个值执行某种操作 C++11

访问string对象中的单个字符有两种方式:一种是使用下标,另一种是使用迭代器

下标和迭代器

下标运算符[]接收的输入参数是string::size_type类型的值,这个参数表示要访问的字符的位置,返回值是该位置上的字符的引用

注意下标范围的有效性,整型值都可以作为索引,如果某个索引是带符号整型类型的值,将自动转换成由string::size_type表达的无符号类型(只要这个带符号整型类型的值不是负数,转换就可以正常进行)

只要字符串不是常量,就能为下标运算符返回的字符赋新值(因为返回的是引用)

建议使用string::size_type类型的下标,控制下标的合法性是程序员的责任(时刻警惕着)


三、标准库类型vector

C++语言既有类模板,也有函数模板

模板本身不是类或函数,相反可以将模板看作为编译器生成类或函数编写的一份说明。编译器根据模板创建类或者函数的过程被称为“实例化”

对于类模板来说,我们通过提供一些额外的信息来指定模板到底实例化成什么样的类,需要提供哪些信息由模板决定。提供信息的方式总是这样:即在模板名字后面跟一对尖括号,在括号内放上信息

值初始化:在初始化时,只提供元素数量而略去初始值。此时库会创建一个值初始化的元素初值,并把它赋给容器中的所有元素。这个初值由元素类型决定。如果元素类型是int,则初值为0。如果元素类型是类类型,那么由类默认初始化

vector v1 默认初始化
vector v2(v1) 拷贝初始化(直接初始化形式) **********
vector v2 = v1 与前一个等价,并不完全等价,差别在explicit构造函数上 ************
vector v3(n, val) n个值为val的元素
vector v4(n) n个值初始化的元素
vector v5{} 列表初始化,也可以进行n个默认值的初始化
vector v5 = {} 与前一个等价,该方式也可以用来进行n个指定值的初始化

vector对象能高效增长(性能不是问题)

范围for循环不能改变其所遍历序列的大小,所以不能使用范围for循环向一个序列中添加元素

只有序列中元素的类型可以比较的时候,才能对序列使用比较运算符

不能用下标来添加元素:下标是用来访问已经存在的元素,不能用来访问不存在的元素

“缓冲区溢出错误”指的是通过下标访问不存在的元素(这只是其中一个原因)

***下标运算符有很多细节需要注意

范围for语句可以在相当的程度上确保下标合法 C++11


四、迭代器介绍

所有的标准库容器都可以使用“迭代器”,但是其中只有少数几种才同时支持“下标运算符”

所有的标准库容器都可以使用迭代器***

有效迭代器包括尾后迭代器

有迭代器的类型都具有返回迭代器的成员,一般是begin和end(cbegin和cend)

迭代器的解引用操作返回的是迭代器所指元素的引用,被解引用的迭代器都必须确实指向一个有效元素

迭代器的移动是通过前置自增和自减运算符来实现的 ??????????一定是前置的才行吗???????????

泛型编程:所有标准库容器的迭代器都定义了== 和!=,但是他们中的大多数都没有定义<运算符。所以养成使用迭代器和!=的习惯

const容器对象只能使用const_iterator,非常量容器对象两个都可以使用,iterator可读可写,const_iterator只能读不能写

C++11中定义了返回const_iterator类型迭代器的两个新函数,cbegin和cend。注意类型的匹配,尽量避免类型转换

vector的动态增长有两个副作用:

  • 1、范围for语句不能向vector对象中添加元素 范围for循环本身就不可以这么用*
  • 2、任何一种可能改变vector对象容量的操作,都会使得指向该容器的迭代器失效 时刻保持更新**

在使用到迭代器的循环体中,不要向迭代器所属的容器中添加元素

迭代器的差值的类型是difference_type,是一种带符号整数。定义在相应的标准库类型中

迭代器的“算术运算”的前提是两个迭代器必须属于同一个容器

?????????算术运算?????????

用迭代器实现二分搜索算法p100,超级简单


五、数组

数组也是存放类型相同的对象的容器,这些对象没有名字,需要通过下标来访问,不支持迭代器。数组的大小确定不变,不能随意向数组中增加元素

数组是一种复合类型,数组的维度必须是一个“常量表达式”。

默认情况下,数组的元素被默认初始化。和内置类型的变量一样,如果在函数内部定义了某种内置类型的数组,那么默认初始化会使数组含有未定义的值。

内置类型的变量的默认初始化的结果是未定义的****

定义数组的时候,必须定义数组的类型,不允许使用auto关键字由初始值的列表推断类型。不存在引用的数组

在对数组进行显示的列表初始化的时候,允许忽略数组的维度。如果维度比提供的初始值的数量大,那么剩下的空间将进行默认初始化

??????上述有疑问??????

我们可以用字符串字面值常量来初始化字符数组,会保留‘\0’符号

不能将数组的内容拷贝给其他数组作为其初始值,也不能用数组为另一个数组赋值

注意理解复杂的数组声明:建议从数组的名字开始从内到外进行阅读

数组声明的阅读方式比较独特,从内到外

int *(&array)[10] 一个数组的引用,该数组中存放int指针

数组的元素可以使用范围for语句或下标来进行访问

数组的下标类型是size_t类型,该类型是一种机器相关的无符号类型,它被设计的足够大以便能表示内存中任意对象的大小。定义在cstddef头文件中,建议通过std命名空间使用该类型

下标越界会引发缓冲区溢出错误

在很多用到数组名字的地方,编译器都会自动将其转换为一个指向数组首元素的指针,sizeof是一个例外

使用auto关键字对数组名初始值进行推导,得到的是指针类型

使用decltype关键字对数组名进行推导,得出的是数组类型(维度都一样)

我们可以通过对尾后元素进行取地址运算来取得数组的尾后指针(这种方法很容易出错)

C++11在iterator头文件中定义了两个可以用来取得数组头指针和尾后指针的标准库函数,begin和end。应该需要通过std使用该函数

上述两个函数的使用方法与获得迭代器的那两个函数的用法不同*

数组的尾后指针是合法的,但是不能进行自增和解引用操作。超出了尾后指针的指针定义是不合法的

指针的差值的类型是ptrdiff_t,其是一种标准库类型, 是定义在cstddef头文件中的机器相关类型,其是一种带符号类型

*size_t和ptrdiff_t都定义在cstddef头文件中,都是机器相关的

对空指针的相关操作都是没有意义的p107

在使用解引用运算符、点运算符等的地方,可以使用括号来适当的进行运算顺序的控制

内置的下标运算和指针运算是可以相互转化的。内置的下标运算符所使用的索引不是无符号类型。这和标准库类型中的下标运算符所用的索引不同

**内置的下标运算符和STL中容器的下标运算符的参数类型是不一致的

在C++中不建议使用C风格字符串

字符串字面值常量是一种通用结构的实例,这种通用结构就是C风格字符串。

C风格字符串不是一种类型,而是为了表达和使用字符串而形成的一种约定俗成的写法。按此习惯写的字符串放在字符数组中并以空字符结束。一般利用指针来操作这些字符串。

cstring中定义了很多可以用于C风格字符串的函数。传给该类函数指针都必须指向C风格字符串

不能直接比较指向C风格字符串的指针,需要使用strcmp函数来实现

目标字符串的大小由调用者指定,在使用strcpy和strcat函数的时候,目标字符串的大小由程序员来估算(极易出错)。所以建议使用C++标准库string类型

字符串字面值常量是有\0结束符的,其就是使用C风格字符串来实现的

为了与旧代码兼容,可以混用string对象和C风格字符串。任何出现字符串字面值常量的地方都可以用以空字符结束的字符数组来替代。允许使用以空字符结束的字符数组来初始化string对象或为string对象赋值。在string对象的加法运算中允许使用以空字符结束的字符数组作为其中的一个运算对象(不能两个运算对象都是);在string对象的复合赋值运算符中允许使用以空字符结束的字符数组作为右侧的运算对象。

但是如果程序某处需要一个C风格字符串,我们无法直接用string对象来代替。为了完成该功能,string对象提供了一个名为c_str的成员函数。该函数返回一个const char *,该指针指向一个以空字符结束的字符数组,这个数组所保存的数据与那个string对象的一样。

我们无法保证c_str返回的数组一直有效。如果string对象改变了,那么其之前返回的数组就会失效。如果想要一直使用该数组,最好将其重新拷贝一份。

我们不允许用一个数组为另一个数组赋值(初始化也不行),也不允许使用vector对象初始化数组,但是我们可以使用数组来初始化vector对象,通过可以接受数组指针对的vector构造函数

尽量使用标准库类型而非数组


六、多维数组

C++没有多维数组,通常所说的多维数组其实是数组的数组

通常把第一个维度称作行,第二个维度称作列

在初始化时,未列出的元素执行默认初始化

*类似情况的默认初始化的结果不是未定义的

*在未给予任何初始值情况下的默认初始化的结果与给予了一些初始值后进行的默认初始化的结果是不同的

使用范围for语句处理多维数组,除了最内层的循环外,其他所有循环的控制变量都应该是引用类型

指针和多维数组:使用auto和decltype来实现更简单一些

类型别名可以简化多维数组的指针,注意对数组别名的理解
using int_array = int[4];
typedef int int_array[4];
int_array表示一个含有四个元素的int数组

猜你喜欢

转载自blog.csdn.net/weixin_39918693/article/details/86563981