目录
一、概述
STL 主要包含容器、算法和迭代器三大部分。
STL 容器包含了绝大多数数据结构,如数组、链表、队列、堆、栈和树等。开发者直接应用这些系统 STL 容器相关函数就可以了,而且这些函数都是带模板参数的,可以适应许多数据元素类型,功能非常强大。STL 算法包含了诸如增、删、改、查和排序等系统函数,开发者可以直接操作这些函数实现相应功能。STL 迭代器类似指针,通过它的有序移动把容器中的元素与算法关联起来,它是实现所有 STL 功能的基础所在。
当然,STL 也包含其他一些内容,如字符串,输入输出流等内容。
二、模板
模板分为函数模板和类模板。
模板参数声明:template<class T>
三、迭代器
迭代器是 STL 重要的核心技术,提供了统一访向容器元素的方法,为编制通用算法提供了坚实的技术基础。
1. 什么是迭代器
迭代器即是指针,可以是所需要的任意类型,它的最大好处是可以使容器和算法分离开来。例如,有两个容器类:MyArray 是某类型数组集合;MyLink 是某类型链表集合。它们都有显示、查询和排序等功能,常规思维是每个容器类中有自己的显示,查询和排序等函数。
仔细分析可得出:不同容器中完成相同功能代码的思路大体是相同的,那么能不能把它们抽象出来,多个容器仅对应一个显示、一个查询,一个排序函数呢?这是泛型思维发展的必然结果,于是迭代器思维就产生了。
【例 1】为数组容器、链表容器编制共同显示函数。
MyArray:
#include<stdio.h>
template<class T>
class MyArray
{
private:
int m_nTotalsize;
//数组总长度
int m_nvalidsize;
//数组有效长度
T*m_pData;
//数据
public:
MyArray(int nsize-3)
//数组默认总长度是 3
m_pData-new T[nSize];
m_nTotalsize-nSize;
m_nValidsize=0;
void Add (T value)//向m_PData添加数据
{
}
int Getsize()//返回数组有效长度
{
return m_nvalidsize;
}
T Get (int pos)
//返回某一位置元素
return m_pDatalpos);
virtual~Myarray()
{
if(m_pData!=NULL)
{
delete []m_pData;
m_pData-NULL;
}
}
}
MyLink 单项链表类初始代码如下所示。
template <class T>
struct Unit
//链表单元
T value;
Unit* next;
田
template<class T>
class MyLink
如
Unit<T>* head;
unit<T>*tail;
unit<T>*prev;
//链表头
//链表尾
废
public;
Mylink()
head-tail-prev=NULL;
void Add (T &value)
//向链表中添加元素
Unit<T>*u-new Unit<T>();
u->value-value;
u->next=NULL;
if(head ==NULL)
head-u;
prev=u;
else
prev->next=u;
prev=u;
tail-u->next;
virtual~MyLink()
if (head!=NULL)
Unit<T>* prev=head;
Unit<T>* next=NULL;
while(prev!=tail)
next=prev->next;
delete prev;
prev=next;
可以看出,MyLink 是模板元素T的链表类,以 struct Unit 为一个个链表单元。那么如何以 MyArray MyLink 为基础完成一个共同显示函数呢?其实非常简单,先从需要出发,逆向考虑,需要先写一个泛型显示函数。
2. 进一步理解迭代器
迭代器的编程思路,如果用图形来描述:
每个容器都应有对应的迭代器,容器通过迭代器共享某一具体算法,某一具体算法不依附于某一具体的容器。迭代器起到一个中间媒介的作用,通过它把容器与算法关联起来。换一句更贴切的话来说,迭代器思维是编制通用泛型算法发展的必然结果,算法通过迭代器依次访问容器中的元素。
STL标准模板库编程的基本步骤:
(1)形成容器元素;
(2)取出所需要的迭代指针:
(3)调用通用算法。
3. STL迭代器
(1)输入迭代器:
istream_iterator
(2)输出迭代器:
ostream_iterator
(3)前向迭代器:
如replace
(4)双向迭代器:
可以向后一次移动一个位置
(5)随机访问迭代器:
具有指针的功能
四、通用容器
1. 堆和栈的区别
(1)堆:链式存储结构,由人来生成和释放(new,malloc,relloc,free),存储数组和对象,效率低。
(2)栈:内存区域是连续的,先进后出,存储局部变量和参数值,又计算机自动生成和使用(内存泄漏),生命周期短,效率高。
2. 容器分类
(1)序列性容器:
按照线性排列来存储某类型值的集合,每个元素都有自己特定的位 置,顺序容器主要有 vector deque 和 list,
vector:
就是动态数组。它是在堆中分配内存,元素连续存放,有保留内存,如果减 少大小后内存也不会释放。新值大于当前大小时才会再分配内存。对最后元素操 作最快(在后面添加删除最快),此时一般不需要移动内存。只有保留内存不够时, 才需要对中间和开始处进行添加删除元素操作,这时需要移动内存,如果你的元素 是结构或是类,那么移动的同时还会进行构造和析构操作。vector 的一大特点是可 直接访问任何元素。
vector(连续的空间存储,可以使用[操作符)快速地访向随机的元素,快速地在末尾插
入元素,但是在序列中间随机地插入,删除元素要慢。而且如果一开始分配的空间不够的
话,有一个重新分配更大空间的过程。
示例:
#include <iostream>
#include<vector>
#include <algorithm>
int main()
{
using namespace std;
创建容器
vector<double> values; //空容器
values.reserve(20); //增加容量20
vector<int> primes{ 2, 3, 5, 7, 11, 13, 17, 19 };//枚举
vector<double> values2(20); //20个0元素
vector<double> values3(20,1); //20个1元素
vector<double> values4(values3); //复制
//访问元素
cout << values[0] << values.at(2) << values.front() << values.back();
/// 增加、插入元素<summary>
values.push_back('S');
values.emplace_back('s');
values.insert(values.begin(), 'T');
values.insert(values.end(), {1.2,3.3,6.5,7,55,12,66,85,35});
values.emplace(values.end(), 9.9);
/// </summary>
/// 删除元素
values.pop_back();
//values.erase(values.rend()-2);
//values.clear();
std::remove(values.begin(), values.end(), 55);
return 0;
}
deque:
与 vector 类似,支持随机访问和快速插入删除,它在容器中某一位置上的操 作所花费的是线性时间。与 vector 不同的是,deque 还支持从开始端插人、删除数 据。由于它主要对前端,后端进行操作,因此也叫做双端队列。
deque(小片的连续,小片间用链表相连,实际上内部有一个 map 的指针,因为知道类
型,所以还是可以使用门,只是速度没有 vector快)快速地访问随机的元素,快速地在开始
和末尾插入元素,随机地插入、删除元素要慢,空间的重新分配要比 vector 快,重新分配空
间后,原有的元素不需要备份。对 deque 的排序操作,可将 deque 先复制到 vector,排序后
再复制回 deque。
list:
又叫链表,是一种双线性列表,只能顺序访问(从前向后或者从后向前),与前面 两种容器类有一个明显的区别就是它不支持随机访问。要访向表中某个下标处的 项需要从表头或表尾处(接近该下标的一端)开始循环。
list(每个元素间用链表相连)访问随机元素不如 vector 快,随机地插人元素比 vector
快,对每个元素分配空间,所以不存在空间不够,重新分配的情况。
示例:
(2)关联式容器:
与前面讲到的顺序容器相比,关联式容器更注重快速和高效地检索 数据的能力。这些容器是根据键值(key)来检索数据的,键可以是值也可以是容器中的某一 成员。这一类中的成员在初始化后都是按一定顺序排好序的。关联式容器主要有 set, multiset.map 和 multimap。
•set:快速查找,不允许重复值。
set 内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比
较快。
• multiset:快速查找,允许重复值。
• map:一对一映射,基于关键字快速查找,不允许重复值。
map 一对一地映射结合,key 不能重复。
•multimap:一对多映射,基于关键字快速查找,允许重复值。
(3)容器适配器:
对已有的容器进行某些特性的再封装,不是一个真正的新容器。1
要有 stack、queue。
• stack:堆栈类,特点是后进先出。
• queue:队列类,特点是先进先出。
3. 容器共性
(1)一般函数
•默认构造函数:提供容器默认初始化的构造函数。
•复制构造函数:将容器初始化为现有同类容器副本的构造函数。
•析构函数:不再需要容器时进行内存整理的析构函数。
• empty: 容器中没有元素时返回 true,否则返回 false。
•max_size: 返回容器中最大元素个数。
• size: 返回容器中当前元素个数。
•operator一: 将一个容器赋给另一个容器。
•operator<: 如果第一个容器小于第二个容器,返回 true,否则返回 false。
•operator<一: 如果第一个容器小于或等于第二个容器,返回 true,否则返回 false,
•operator>: 如果第一个容器大于第二个容器,返回 true,否则返回 false。
•operator>一: 如果第一个容器大于或等于第二个容器,返回 true,否则返回 false,
•operator==: 如果第一个容器等于第二个容器,返回 true,否则返回 false。
• operator!=: 如果第一个容器不等于第二个容器,返回 true,否则返回 false。
• swap: 交换两个容器的元素。
(2)顺序容器和关联容器共有函数
• begin:该函数有两个版本,返回 iterator 或 const_iterator,返回容器第一个元素选
代器指针。
• end:该函数有两个版本,返回 iterator 或 const_iterator,返回容器最后一个元素剧
面一位的迭代器指针。
• rbegin:该函数有两个版本,返回 reverse_iterator 或 const_reverse_iterator,返回答
器最后一个元素的迭代器指针。
• rend:该函数有两个版本,返回 reverse_iterator 或 const_reverse_iterator,返回容料
首个元素前面一位的迭代器指针。
• erase:从容器中清除一个或几个元素。
• clear:清除容器中所有元素。