此专栏为移动机器人知识体系下的编程语言中的 C {\rm C} C++从入门到深入的专栏,参考书籍:《深入浅出 C {\rm C} C++》(马晓锐)和《从 C {\rm C} C到 C {\rm C} C++精通面向对象编程》(曾凡锋等)。
11.标准模板库
11.1 泛型化编程与STL简介
-
泛型化编程思想是一种将程序要处理的数据类型参数化的设计思想模式;
-
泛型化编程的基本思想:在保证效率的前提下,将算法从具体的应用中抽象出来而创建一个通用的算法库;
-
使用泛型化编程可以最大限度地重用代码、保护类型的安全性及提高性能;
-
C {\rm C} C++支持泛型化编程是通过模板技术实现的,典型的是 S T L {\rm STL} STL;
-
C {\rm C} C++标准库和 S T L {\rm STL} STL的主要内容如下:
-
S T L {\rm STL} STL主要组件:
- 容器 ( c o n t a i n e r s ) ({\rm containers}) (containers):容器是可容纳一些数据的模板类,可以将其看作一种数据结构, S T L {\rm STL} STL包含很多数据结构,如:链表 ( l i s t ) ({\rm list}) (list)、向量 ( v e c t o r ) ({\rm vector}) (vector)、队列 ( q u e u e ) ({\rm queue}) (queue)、堆栈 ( s t a c k ) ({\rm stack}) (stack)等, s t r i n g {\rm string} string类也可看作一个容器;
- 算法 ( a l g o r i t h m s ) ({\rm algorithms}) (algorithms):包含了约 70 70 70个通用算法,用于操纵各种容器;
- 迭代器 ( i t e r a t o r s ) ({\rm iterators}) (iterators):迭代器用于遍历 S T L {\rm STL} STL容器中的部分或全部元素,从而操作容器中的数据;
11.2 STL的使用
-
在 S T L {\rm STL} STL中,容器按照存储结构分为:顺序容器 ( s e q u e n c e c o n t a i n e r ) ({\rm sequence\ container}) (sequence container)、关联容器 ( a s s o c i a t i v e c o n t a i n e r ) ({\rm associative\ container}) (associative container);
- 顺序容器:将其中的对象组织成一个有限线性的结构,所有对象都是相同类型的元素, S T L {\rm STL} STL中包含三种基本的顺序容器:线性表 ( l i s t ) ({\rm list}) (list)、向量 ( v e c t o r ) ({\rm vector}) (vector)、双向队列 ( d e q u e ) ({\rm deque}) (deque);
- 关联容器:通过其内部处理机制来将进入其中的元素进行一定的排列,以使其具有数据的快速检索能力,一般情况下,这种快速检索是基于 k e y {\rm key} key值的,即一个 k e y {\rm key} key对应一个或多个元素; S T L {\rm STL} STL有 4 4 4种关联容器:当一个 k e y {\rm key} key对应一个 v a l u e {\rm value} value时,使用集合 ( s e t ) ({\rm set}) (set)和映射 ( m a p ) ({\rm map}) (map),若同一 k e y {\rm key} key对应多个元素,使用多集合 ( m u l t i s e t ) ({\rm multiset}) (multiset)和多映射 ( m u l t i m a p ) ({\rm multimap}) (multimap);
-
S T L {\rm STL} STL容器及对应的头文件:
数据结构 数据结构 数据结构 描述 描述 描述 头文件 头文件 头文件 向量 ( v e c o t r ) 向量{\rm (vecotr)} 向量(vecotr) 连续存储的元素集合 , 类似一维动态数组 , 为顺序容器 连续存储的元素集合,类似一维动态数组,为顺序容器 连续存储的元素集合,类似一维动态数组,为顺序容器 < v e c t o r > {\rm vector}> vector> 列表 ( l i s t ) 列表({\rm list}) 列表(list) 双向链表 , 由结点组成 , 每个结点包含一个元素 双向链表,由结点组成,每个结点包含一个元素 双向链表,由结点组成,每个结点包含一个元素 < l i s t {\rm list} list> 双队列 ( d e q u e ) 双队列({\rm deque}) 双队列(deque) 连续存储一组指针 , 指针指向不同元素 , 本质为数组 , 支持下标操作 连续存储一组指针,指针指向不同元素,本质为数组,支持下标操作 连续存储一组指针,指针指向不同元素,本质为数组,支持下标操作 < d e q u e {\rm deque} deque> 集合 ( s e t ) 集合({\rm set}) 集合(set) 由结点组成的红黑树 , 每个结点包含一个元素 , 结点间以某种作用于 元素对的谓词排列 ; 任何两个不同元素不能拥有相同的次序 \begin{aligned}&由结点组成的红黑树,每个结点包含一个元素,结点间以某种作用于\\&元素对的谓词排列;任何两个不同元素不能拥有相同的次序\end{aligned} 由结点组成的红黑树,每个结点包含一个元素,结点间以某种作用于元素对的谓词排列;任何两个不同元素不能拥有相同的次序 < s e t {\rm set} set> 多重集合 ( m u l t i s e t ) 多重集合({\rm multiset}) 多重集合(multiset) 允许两个元素拥有相同次序的集合 允许两个元素拥有相同次序的集合 允许两个元素拥有相同次序的集合 < s e t {\rm set} set> 栈 ( s t a c k ) 栈({\rm stack}) 栈(stack) 先进后出数据结构的实现 先进后出数据结构的实现 先进后出数据结构的实现 < s t a c k {\rm stack} stack> 队列 ( q u e u e ) 队列({\rm queue}) 队列(queue) 先进先出数据结构的实现 先进先出数据结构的实现 先进先出数据结构的实现 < q u e u e {\rm queue} queue> 优先队列 ( p r i o r i t y _ q u e u e ) 优先队列({\rm priority\_queue}) 优先队列(priority_queue) 元素的次序是由作用于所存储的值对上的某种谓词决定的一种队列 元素的次序是由作用于所存储的值对上的某种谓词决定的一种队列 元素的次序是由作用于所存储的值对上的某种谓词决定的一种队列 < q u e u e {\rm queue} queue> 映射 ( m a p ) 映射({\rm map}) 映射(map) 由 { 键 , 值 } 对组成的集合 , 以某种作用于键对上的谓词排列 由\{键,值\}对组成的集合,以某种作用于键对上的谓词排列 由{键,值}对组成的集合,以某种作用于键对上的谓词排列 < m a p {\rm map} map> 多重映射 ( m u l t i m a p ) 多重映射({\rm multimap}) 多重映射(multimap) 允许键对有相同次序的映射 允许键对有相同次序的映射 允许键对有相同次序的映射 < m a p {\rm map} map> -
声明容器语法格式:
容器<存储类型> 实例名
-
容器实例 1 1 1:随机产生一组数值,将其存储后输出 ( e x a m p l e 11 _ 1. c p p ) ({\rm example11\_1.cpp}) (example11_1.cpp)。
/** * 作者:罗思维 * 时间:2024/03/28 * 描述:随机产生一组数值,将其存储后输出。 */ #include <iostream> #include <vector> #include <ctime> using namespace std; int main() { vector<int> arrNum; // 声明一个vector实例,其存储的元素为int; srand((unsigned) time(NULL)); // 初始化随机数发生器种子; for (unsigned int i = 0; i < 10; i++) { arrNum.push_back(rand()); // 将随机数存入arrNum种,push_back()函数将元素加入vector尾部; } for (unsigned int i = 0; i < arrNum.size(); i++) { // 输出所有值; cout << arrNum[i] << " "; } cout << endl; cout << "元素个数:" << arrNum.size() << endl; cout << "第三个元素为:" << arrNum.at(2) << endl; // 输出特定元素; arrNum.pop_back(); // 删除最后一个元素; for (unsigned int i = 0; i < arrNum.size(); i++) { // 输出所有值; cout << arrNum[i] << " "; } cout << endl; return 0; }
-
容器实例 2 2 2:利用 S T L {\rm STL} STL的 l i s t {\rm list} list类对字符串进行操作 ( e x a m p l e 11 _ 2. c p p ) ({\rm example11\_2.cpp}) (example11_2.cpp)。
/** * 作者:罗思维 * 时间:2024/03/28 * 描述:利用STL的list类对字符串进行操作。 */ #include <iostream> #include <list> using namespace std; int main() { // 声明list<string>模板类的一个实例; list<string> myStrList; // 使用list的成员函数push_pack和push_front插入一个元素到list中; myStrList.push_back("C"); myStrList.push_back("C++"); myStrList.push_front("Python"); myStrList.push_front("Java"); cout << "myStrList.size() = " << myStrList.size() << endl; return 0; }
-
迭代器本质上是指针的泛化,通过迭代器可以以相同的方式处理不同的数据结构(容器), S T L {\rm STL} STL有 5 5 5种迭代器:
- 输入迭代器 ( i n p u t i t e r a t o r ) ({\rm input\ iterator}) (input iterator):输入是指向迭代器种输入数据,这些数据来源于容器,输入迭代器可以读取容器中的数据,将数据传送给程序,不改变容器中元素的值;
- 输出迭代器 ( o u p u t i t e r a t o r ) ({\rm ouput\ iterator}) (ouput iterator):输出是指将信息从程序中传输给容器,输出迭代器对于容器来说是输入;
- 正向迭代器 ( f o r w a r d i t e r a t o r ) ({\rm forward\ iterator}) (forward iterator):亦称为向前迭代器,正向迭代器只使用 + + ++ ++操作符来遍历容器,因此它每次沿容器中的元素位置向前移动一个元素;
- 双向迭代器 ( b i − d i r e c t i o n a l i t e r a t o r ) ({\rm bi-directional\ iterator}) (bi−directional iterator):具有正向迭代器的所有特征,同时支持 − − -- −−操作符,可以向后移动一个元素;
- 随机访问迭代器 ( r a n d o m a c c e s s i t e r a t o r ) ({\rm random\ access\ iterator}) (random access iterator):具有双向迭代器的所有特性,同时支持随机访问和比较指针大小操作;
-
迭代器的类被封装在命名空间 s t d {\rm std} std中,迭代器主要由头文件:< u t i l i t y {\rm utility} utility>、< i t e r a t o r {\rm iterator} iterator>、< m e m o r y {\rm memory} memory>组成;
- < u t i l i t y {\rm utility} utility>包括 S T L {\rm STL} STL中几个常用模板的声明;
- < i t e r a t o r {\rm iterator} iterator>中提供了迭代器使用的许多方法;
- < m e m o r y {\rm memory} memory>负责为容器中的元素分配存储空间,同时为某些算法执行期间产生的临时对象提供机制;
-
迭代器实例:利用迭代器操作向量 ( e x a m p l e 11 _ 3. c p p ) ({\rm example11\_3.cpp}) (example11_3.cpp)。
/** * 作者:罗思维 * 时间:2024/03/28 * 描述:利用迭代器操作向量。 */ #include <iostream> #include <vector> #include <iterator> #include <ctime> using namespace std; int main() { vector<int> arrNum; // 声明一个vector实例,存储的元素为int型; srand((unsigned)time(NULL)); // 初始化随机数发生器种子; for (int i = 0; i < 10; i++) { arrNum.push_back(rand()); // 将随机生成的数存入arrNum向量容器; } // 声明向量容器的迭代器并指向容器的第一个元素; vector<int>::iterator ite_vec = arrNum.begin(); while (ite_vec != arrNum.end()) { // 利用迭代器来输出容器中的元素值; cout << *ite_vec << " "; ite_vec++; } return 0; }
-
S T L {\rm STL} STL提供一系列高效的通用算法, S T L {\rm STL} STL算法部分主要由头文件:< a l g o r i t h m {\rm algorithm} algorithm>、< n u m e r i c {\rm numeric} numeric>、< f u n c t i o n a l {\rm functional} functional>组成:
- < a l g o r i t h m {\rm algorithm} algorithm>:由大量的模板函数组成,这些函数大部分都是独立的,常用到的功能涉及:比较、交换、查找、遍历操作、复制、修改、移除、反转、排序、合并等;
- < n u m e r i c {\rm numeric} numeric>:包括几个在序列上面进行简单数学运算的模板函数;
- < f u n c t i o n a l {\rm functional} functional>:定义了一些模板类,用于声明函数对象;
-
算法实例:利用 S T L {\rm STL} STL的 l i s t {\rm list} list类对字符串进行操作 ( e x a m p l e 11 _ 4. c p p ) ({\rm example11\_4.cpp}) (example11_4.cpp)。
/** * 作者:罗思维 * 时间:2024/03/28 * 描述:利用STL的list类对字符串进行操作。 */ #include <iostream> #include <string> #include <list> #include <algorithm> using namespace std; void PrintElement(string& str) { cout << str << endl; } int main() { // 声明list容器myStrList; list<string> myStrList; myStrList.push_back("C"); // 在尾部插入; myStrList.push_back("C++"); myStrList.push_front("Python"); // 在头部插入; myStrList.push_front("Java"); for_each(myStrList.begin(), myStrList.end(), PrintElement); return 0; }
-
S T L {\rm STL} STL常用的算法:
- b i n a r y _ s e a r c h {\rm binary\_search} binary_search:在有序序列中查找 v a l u e {\rm value} value,如果找到,返回 t r u e {\rm true} true,否则返回 f a l s e {\rm false} false;
- c o p y {\rm copy} copy:复制序列;
- c o p y _ b a c k w a r d {\rm copy\_backward} copy_backward:按照元素顺序进行反向复制;
- c o u n t {\rm count} count:利用等于操作符,把标志范围类的元素与输入的值进行比较,并返回相等元素的个数;
- c o u n t _ i f {\rm count\_if} count_if:对于标志范围类的元素应用输入的操作符,返回结果为 t r u e {\rm true} true的次数;
- e q u a l {\rm equal} equal:如果两个序列在范围内的元素都相等,则返回 t r u e {\rm true} true,否则返回 f a l s e {\rm false} false;
- f i l l {\rm fill} fill:赋予范围内的每个元素相同的输入值;
- f i l l _ n {\rm fill\_n} fill_n:将输入的值赋予 b e g i n {\rm begin} begin到 b e g i n + n {\rm begin+n} begin+n范围内的元素;
- f i n d {\rm find} find:利用底层元素的等于操作符,将范围内的元素与输入的值进行比较,当匹配时,结束搜索,返回该元素的一个输入迭代器;
- f o r _ e a c h {\rm for\_each} for_each:依次对范围内的所有元素执行输入的函数;
- m a x {\rm max} max:返回两个元素中较大的一个;
- m a x _ e l e m e n t {\rm max\_element} max_element:返回一个 i t e r a t o r {\rm iterator} iterator,指出序列中的最大元素;
- m i n {\rm min} min:两个元素中的较小值;
- m i n _ e l e m e n t {\rm min\_element} min_element:返回一个 i t e r a t o r {\rm iterator} iterator,指出序列中的最小元素;
- m e r g e {\rm merge} merge:合并两个有序序列,并存放到另外一个序列;
- r e m o v e {\rm remove} remove:删除在范围内的所有指定的元素;
- r e m o v e _ i f {\rm remove\_if} remove_if:删除所有范围内输入操作结果为 t r u e {\rm true} true的元素;
- r e p l a c e {\rm replace} replace:将范围内的所有等于 o l d _ v a l u e {\rm old\_value} old_value的元素都用 n e w _ v a l u e {\rm new\_value} new_value替代;
- r e p l a c e _ i f {\rm replace\_if} replace_if:将范围内的所有操作结果为 t r u e {\rm true} true的元素用新值替代;
- r e v e r s e {\rm reverse} reverse:将范围内的元素重新按反序排列;
- s e a r c h {\rm search} search:给出两个范围,返回一个 i t e r a t o r {\rm iterator} iterator,指向在范围内第一次出现子序列的位置;
- s e a r c h _ n {\rm search\_n} search_n:在范围内查找 v a l u e {\rm value} value出现 n {\rm n} n次的子序列;
- s o r t {\rm sort} sort:以升序重新排列范围内的元素;
- s w a p {\rm swap} swap:交换存储在两个对象中的值;
- t r a n s f o r m {\rm transform} transform:将输入的操作作用在范围内的每个元素上,并产生一个新的序列;