从今天开始我会开始更新一系列关于STL的文章链接补充在这节文章的后面,STL也就是标准模板库,是每一个学习c++的程序员必须要接触的知识,在面试,工作中也是需要必须掌握的内容,话不多说我们开始今天的第一期!

文章目录

STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。从程序员角度看,STL是由一些可适应不同需求的群集类别和一些能够在这些数据群集上运作的算法构成。STL内部的所有组件都由template构成,所以其元素可以是任意型别。
STL 就是借助模板把常用的数据结构及其算法都实现了一遍,并且做到了数据结构和算法的分离。例如,vector 的底层为顺序表(数组),list 的底层为双向链表,deque 的底层为循环队列,set 的底层为红黑树,hash_set 的底层为哈希表。
STL提供了六大组件,彼此之间可以组合套用,他们分别为 :容器、算法、迭代器、仿函数、适配器)、空间配置器
最关键的是容器,算法,迭代器
容器:用来管理某类对象的集合。容纳特定数据类型对象的集合,STL容器是将最常用的一些数据结构实现出来.包含了许多数据结构,如:vector,queue,statck…,string也可以看作是一个容器.
分类:容器用来管理一组元素,为了适应不同需要,STL根据数据在容器中排列的特性,容器可分为序列式容器和关联式容器两种.
序列式容器:可序群集,其中每个元素都有固定位置——取决于插入时机和地点,与元素的值没有关系,如果你以追加方式对一个群集置入n个元素,它们的排列次序将和置入次序一致。如vector,deque,list
关联式容器:已序群集,元素位置取决于特定的排序准则,和插入顺序无关。如果你将n个元素置入这样的群集中,它们的位置取决于元素值,和插入次序无关。STL提供了4个这样的容器:set,map,multiset,multimap。 关联式容器也可被视为特殊的序列式容器,因为已序群集正是根据某个排序准则排列而成。
算法:用来处理群集内的元素。它们可以出于不同的目的而搜寻,排序,修改,使用那些元素。是一种应用在容器上以各种方法处理其内存的行为或功能,如sort(排序),copy(拷贝)…,算法由模板函数体现,这些函数不是容器类的成员函数,是独立的函数,它们可以用于STL容器,也可以用于普通的C++数组等.
头文件:#include
1)变序型队列算法: 可以改变容器内的数据;
2)非变序型队列算法:处理容器内的数据而不改变他们;
3)排序值算法:包涵对容器中的值进行排序和合并的算法,还有二叉搜索算法 ;
4)通用数值算法:此种算法不多,涉及到专业领域中有用的算术操作,独立包涵于头文件
通俗来讲,迭代器就是指示器。迭代器技术能够使程序非常快捷地实现对 STL 容器中内容的反复访问。反复访问意味着一次可以访问一个或多个元素。
迭代器:用来在一个对象群集的元素上进行遍历动作。这个对象群集或许是个容器,或许是容器的一部分。迭代器的主要好处是,为所有容器提供了一组很小的公共接口,利用这个接口,某项操作就可以行进至群集内的下一个元素。每一种容器都提供了自己的迭代器,而这些迭代器了解该种容器的内部结构,所以能够知道如何正确行进。迭代器的接口和一般指针差不多(同指针理解,但不是指针,也没有哪本书说明迭代器和指针有关系)。 可将其看作是一个指向容器中元素的普通指针,用迭代器来访问容器中的元素,是它将算法和容器连接在一起,每个容器都有自己的迭代器,只有容器自己才知道如何访问自己的元素.
STL 定义了 5 种类型的指示器,并根据其使用方法予以命名。每种容器都支持某种类别的迭代器。常见的迭代器包括输入、输出、前向、双向和随机接入等类别:
1) 前向迭代器
假设 p 是一个前向迭代器,则 p 支持 ++p,p++,*p 操作,还可以被复制或赋值,可以用 == 和 != 运算符进行比较。此外,两个正向迭代器可以互相赋值。
2) 双向迭代器
双向迭代器具有正向迭代器的全部功能,除此之外,假设 p 是一个双向迭代器,则还可以进行 --p 或者 p-- 操作(即一次向后移动一个位置)。
3) 随机访问迭代器
随机访问迭代器具有双向迭代器的全部功能。除此之外,假设 p 是一个随机访问迭代器,i 是一个整型变量或常量,则 p 还支持以下操作:
此外,两个随机访问迭代器 p1、p2 还可以用 <、>、<=、>= 运算符进行比较。另外,表达式 p2-p1 也是有定义的,其返回值表示 p2 所指向元素和 p1 所指向元素的序号之差(也可以说是 p2 和 p1 之间的元素个数减一)。
| 迭代器 | 功能 | 描述 |
| 输入迭代器 | 提供对数据的只读访问 | 只读,支持++、==、!= |
| 输出迭代器 | 提供对数据的只写访问 | 只写,支持++ |
| 前向迭代器 | 提供读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
| 双向迭代器 | 提供读写操作,并能向前和向后操作 | 读写,支持++、–, |
| 随机访问迭代器 | 提供读写操作,并能以跳跃的方式访问容器的任意数据,是功能最强的迭代器 | 读写,支持++、–、[n]、-n、<、<=、>、>= |
行为类似函数,可作为算法的某种策略。从实现角度来看,仿函数是一种重载了operator()的class 或者class template
仿函数:仿函数是泛型编程强大威力和纯粹抽象概念的又一个例证。任何东西,只要其行为像函数(指可以“使用小括号传递参数,藉以调用某个东西”),它就是一个函数。如果你定义了一个对象,行为像函数,它就可以被当做函数来用。 将函数封装在一个对象中,使得它可以作为参数传递给合适的STL算法,从而使得算法的功能得以扩展,可以把它当作函数来使用, 其实就是在类里面定义重载()运算符,让一个类能象一个函数一样使用.
仿函数比起一般函数有下列优点
一种用来修饰容器或者仿函数或迭代器接口的东西。STL提供的queue和stack,虽然看似容器,但其实只能算作一种容器配接器,因为他们的底部完全借助deque,所有操作全都由底层的duque供应。
负责空间的配置与管理。从实现角度看,配置器是一个实现了动态空间配置、空间管理、空间释放的class tempalte.
在 C++ 中如果定义一个数组,可以采用如下方式:
int arr[n];
这种定义数组的方法需要事先确定好数组的长度,即 n 必须为常量,这意味着,如果在实际应用中无法确定数组长度,则一般会将数组长度设为可能的最大值,但这极有可能导致存储空间的浪费。
所以除此之外,还可以采用在堆空间中动态申请内存的方法,此时长度可以是变量:
int *p=new int[n]
这种定义方式可根据变量 n 动态申请内存,不会出现存储空间浪费的问题。但是,如果程序执行过程中出现空间不足的情况时,则需要加大存储空间,此时需要进行如下操作:
- 新申请一个较大的内存空间,即执行:
- int * temp = new int[m];
- 将原内存空间的数据全部复制到新申请的内存空间中,即执行:
- memecpy(temp, p, sizeof(int)*n);
- 将原来的堆空间释放,即执行:
- delete [] p;
- p = nullptr;
如果使用STL后就会简单很多
- int main()
- {
- vector <int> a; //定义 a 数组,当前数组长度为 0,但和普通数组不同的是,此数组 a 可以根据存储数据的数量自动变长。
- //向数组 a 中添加 10 个元素
- for (int i = 0; i < 10; i++)
- a.push_back(i);
-
- cout << a.size() << endl;//显示a数组大小
- //显示插入的值
- for (int i = 0; i < a.size(); ++i)
- {
- cout << a[i] << " " ;
- }
- //还可以手动调整数组 a 的大小
- a.resize(100);
- cout << endl<
size() << endl; - a[90] = 100;//将100赋值给a[90]
-
- //还可以直接删除数组 a 中所有的元素,此时 a 的长度变为 0
- a.clear();
- cout << a.size() << endl;
- //重新调整 a 的大小为 20,并存储 20 个 -1 元素。
- a.resize(20, -1);
- cout << a.size() << endl;
- for (int i = 0; i < a.size(); ++i)
- {
- cout << a[i] << " ";
- }
- return 0;
-
- }

再举一个栗子:
- int main()
- {
- // 创建一个向量存储 int
- vector<int> vec;
- int i;
-
- // 显示 vec 的原始大小
- cout << "vector size = " << vec.size() << endl;
-
- // 推入 5 个值到向量中
- for(i = 0; i < 5; i++){
- vec.push_back(i);
- }
-
- // 显示 vec 扩展后的大小
- cout << "extended vector size = " << vec.size() << endl;
-
- // 访问向量中的 5 个值
- for(i = 0; i < 5; i++){
- cout << "value of vec [" << i << "] = " << vec[i] << endl;
- }
-
- // 使用迭代器 iterator 访问值
- vector<int>::iterator v = vec.begin();
- while( v != vec.end()) {
- cout << "value of v = " << *v << endl;
- v++;
- }
- /*使用for循环
- for (v; v != vec.end(); ++v)
- {
- cout << "value of v = " << *v << endl;
- }
- */
- return 0;
- }
结果为:
- vector size = 0
- extended vector size = 5
- value of vec [0] = 0
- value of vec [1] = 1
- value of vec [2] = 2
- value of vec [3] = 3
- value of vec [4] = 4
- value of v = 0
- value of v = 1
- value of v = 2
- value of v = 3
- value of v = 4
关于上面实例中所使用的各种函数,有几点要注意:
后续开始从STL不同的容器,各种算法函数实现以及用法,迭代器的使用来进行讲解,更新这一系列。