文章目录
C++ STL标准库系列文章:
[STL] 1.简介
[STL] 2.序列容器 固定数组array(C++ 11)
[STL] 3.序列容器 动态数组vector
[STL] 4.序列容器 双端队列deque
[STL] 5.序列容器 双向链表list
[STL] 6.序列容器 单向链表forward_list(C++ 11)
[STL] 7.适配器简介
[STL] 8.容器适配器 栈stack
[STL] 9.容器适配器 队列queue
[STL] 10.容器适配器 优先队列priority_queue
[STL] 11.关联容器 集合set
[STL] 12.关联容器 映射map
[STL] 13.关联容器 多重集合multiset
[STL] 14.关联容器 多重映射multimap
[STL] 15.关联容器 无序集合unordered_set(C++ 11)
[STL] 16.关联容器 无序集合unordered_map(C++ 11)
[STL] 17.仿函数functor与函数对象
[STL] 18.预定义函数对象、仿函数适配器
[STL] 19.算法algorithm
[STL] 20.迭代器适配器
[STL] 21.空间配置器allocator
1. 什么是STL?
标准模板库 - 维基百科,自由的百科全书 (wikipedia.org)
标准模板库(英文:Standard Template Library,缩写:STL),是一个C++软件库,大量影响了C++标准程序库但并非是其的一部分。其中包含4个组件,分别为算法、容器、函数、迭代器。[1]
模板是C++程序设计语言中的一个重要特征,而标准模板库正是基于此特征。标准模板库使得C++编程语言在有了同Java一样强大的类库的同时,保有了更大的可扩展性。
2. STL的优势
- 功能强大、代码精致
STL由惠普实验室的无数大牛们开发,设计精巧,功能强大,集成了优秀的算法。
- 高可重用性
ST广泛使用模板泛型编程,代码具有高度的复用性。
- 高性能、工业强度
ST提供的算法具有工业强度,高性能,用它可开发出性能高效的应用程序。
- 开源跨平台
STL跨平台,而且开源,开发者可以很容易借鉴与扩展。
3. STL版本?
HP STL
HP STL是Alexandar Stepanov在惠普Palo Alto实验室工作时,与Meng Lee合作完成的。HP STL是C++ STL的第一个实现版本,而且是开放源码。其它版本的C++ STL一般是以HP STL为蓝本实现出来的。
SGI STL
由Silicon Graphics Computer Systems公司参照HP STL实现,主要设计者仍然是STL之父Alexandar Stepanov,被Linux的C++编译器GCC所采用。SGI STL是开源软件,源码可读性甚高。
STLport
为了使SGI STL的基本代码都适用于VC++和C++ Builder等多种编译器,俄国人Boris Fomitchev建立了一个free项目来开发STLport,此版本STL是开放源码的。
P.J.Plauger STL
由P.J.Plauger参照HP STL实现出来,被Visual C++编译器所采用,但不是开源的。
Rouge Wave STL
由Rouge Wave公司参照HP STL实现,用于Borland C++编译器中,这个版本的STL也不是开源的。
4. STL的六大组件
1、容器(Containers):各种数据结构,如Vector,List,Deque,Set,Map,用来存放数据,STL容器是一种Class Template,就体积而言,这一部分很像冰山载海面的比率。
2、算法(Algorithms):各种常用算法如Sort,Search,Copy,Erase,从实现的角度来看,STL算法是一种Function Templates。
3、迭代器(Iterators):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,共有五种类型,以及其它衍生变化,从实现的角度来看,迭代器是一种将:Operators*,Operator->,Operator++,Operator–等相关操作予以重载的Class Template。所有STL容器都附带有自己专属的迭代器——是的,只有容器设计者才知道如何遍历自己的元素,原生指针(Native pointer)也是一种迭代器。
4、仿函数(Functors): 行为类似函数,可作为算法的某种策略(Policy),从实现的角度来看,仿函数是一种重载了Operator()的Class 或 Class Template。一般函数指针可视为狭义的仿函数。
5、配接器(适配器)(Adapters):一种用来修饰容器(Containers)或仿函数(Functors)或迭代器(Iterators)接口的东西,例如:STL提供的Queue和Stack,虽然看似容器,其实只能算是一种容器配接器,因为 它们的底部完全借助Deque,所有操作有底层的Deque供应。改变Functor接口者,称为Function Adapter;改变Container接口者,称为Container Adapter;改变Iterator接口者,称为Iterator Adapter。配接器的实现技术很难一言蔽之,必须逐一分析。
6、分配器(Allocators):负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的Class Template。
——《STL源码剖析》
5. 学习建议
路线:
- C++数据结构
- C++模板
- STL标准库
- Boost库
资源:
Containers - C++ Reference (cplusplus.com)
6. 为什么需要迭代器?
#include "stdafx.h"
#include<iostream>
using namespace std;
/* 思考题一
现在有一个数组 int arr[5] = { 1,2,3,4,5 };
void Print(int *begin, int *end)
{
//此处编写代码
}
让 Print(arr, arr + 5); 实现打印所有元素?
*/
/*
void Print(int *begin, int *end)
{
//此处编写代码
for (int *p = begin; p != end; ++p)//利用指针的++,依次访问每个元素
{
cout << *p << " ";
}
cout << endl;
}
*/
/* 思考题二
现在有一个链表,有5个Node节点:
struct Node
{
int data;
Node* next;
};
void Print(Node *begin, Node *end)
{
//此处编写代码
}
让 Print(pHead, pTail ); 实现打印所有元素?
*/
struct Node
{
int data;
Node* next;
};
ostream & operator <<(ostream& o,const Node &node)
{
o << node.data << endl;
return o;
}
/*
void Print(Node *begin, Node *end)
{
//此处编写代码
for (Node *p =begin; p!= end ; p=p->next) //指向下一个节点
{
cout << (*p).data << " ";
}
cout << endl;
}
*/
//思考题三
//写出一个通用的Print 函数模板
template<class T>
void Print(T begin, T end)
{
//此处编写代码
for (T p = begin; p != end; ++p)
{
cout << *p << " ";
}
cout << endl;
}
int main()
{
//题目一测试
int arr[5] = { 1,2,3,4,5 };
Print(arr, arr + 5); //arr + 5是指向 最后一个元素下一个的位置
//Print<int *>(arr, arr + 5);//调用模板函数
//题目二测试
Node n1;
n1.data = 11;
Node n2;
n2.data =22;
Node n3;
n3.data = 33;
Node n4;
n4.data = 44;
Node n5;
n5.data = 55;
n1.next = &n2;
n2.next = &n3;
n3.next = &n4;
n4.next = &n5;
n5.next = NULL;
Print(&n1, n5.next);
return 0;
}
7. 自己实现迭代器iterator
迭代器(iterator)是指针的抽象,它允许程序员以相同的方式处理不同的数据结构(容器)。
迭代器支持的一些基本操作:
=
:迭代器赋值==
:比较迭代器的相等性!=
:比较迭代器的不等性++
:迭代器向后移动--
:迭代器向前移动*
:解引用,返回迭代器指向的元素
#include "stdafx.h"
#include<iostream>
using namespace std;
//思考题三
//写出一个通用的Print 函数模板
template<class T>
void Print(T begin, T end)
{
//此处编写代码
for (T p = begin; p != end; ++p)
{
cout << *p << " ";
}
cout << endl;
}
//虽然数组不需要迭代器,因为指针就是一种特殊的迭代器
//也可以设计一个数组迭代器
class ArrayIterator
{
public:
ArrayIterator(int *p) : _p(p) {}
ArrayIterator & operator++()
{
++_p;//指针就是++指向下一个
return *this ;
}
bool operator!=(const ArrayIterator & it)
{
return _p != it._p;
}
int& operator*()
{
return *_p;
}
//private:
int * _p;
};
//考虑设计一个中间类,让它支持operator ++,实际实现是链表的p=p->next
//链表迭代器
class ListIterator
{
public:
ListIterator(Node *p) : _p(p) {}
ListIterator & operator++()
{
//链表的真正操作
_p = _p->next;
return *this;
}
bool operator!=(const ListIterator & it)
{
return _p != it._p;
}
Node& operator*( )
{
return *_p;
}
//private:
Node * _p;
};
int main()
{
//使用链表迭代器
ListIterator begin(&n1);
ListIterator end(n5.next);
Print(begin, end);
return 0;
}
8. STL中的5种迭代器
- 输入迭代器(Input iterator)
- 输出迭代器(Output Iterator)
- 前向迭代器(Forward iterator)
- 双向迭代器(Bidirectional iterator)
- 随机访问迭代器(Random Access iterator)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-on3WXlhN-1620021492142)(C:\Users\tangx\AppData\Roaming\Typora\typora-user-images\image-20210503133856493.png)]
不同的数据结构(容器)支持的迭代器类型不同,比如数组结构可支持随机访问迭代器,但是链表结构就无法支持,链表往往设计为双向迭代器。
9. STL中的iterator类
iterator
类是一个基类模板,可用于从它派生迭代器类。它不提供迭代器具有的任何功能,仅提供了些默认成员类型,这些并不是迭代器类型必须需具备的,它们可能是有用的。
定义:
iterator - C++ Reference (cplusplus.com)
template <class Category, class T, class Distance = ptrdiff_t,
class Pointer = T*, class Reference = T&>
struct iterator {
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
typedef Category iterator_category;
};
Category
迭代器所属的类别。 它必须是以下迭代器标签之一:
T
迭代器指向的元素类型。
Distance
键入以表示两个迭代器之间的差异。
Pointer
类型,表示指向迭代器指向的元素的指针。
Reference
类型,表示对迭代器指向的元素的引用。
示例代码:
#include "stdafx.h"
#include<iostream>
#include<iterator>
using namespace std;
class A
{
public:
typedef int MYINT; //内部类型
using MYFLOAT = float; //内部类型
};
class MyIterator : public std::iterator<std::random_access_iterator_tag, int>
{
int* p;
public:
MyIterator(int* x) :p(x) {}
MyIterator(const MyIterator& mit) : p(mit.p) {}
MyIterator& operator++() { ++p; return *this; }
MyIterator operator++(int) { MyIterator tmp(*this); operator++(); return tmp; }
bool operator==(const MyIterator& rhs) const { return p == rhs.p; }
bool operator!=(const MyIterator& rhs) const { return p != rhs.p; }
int& operator*() { return *p; }
//int &operator[](int index) { return *(p + index); }
};
int main()
{
//A::MYINT a = 100;
//A::MYFLOAT b = 100;
//定义一个迭代器对象
iterator< input_iterator_tag , int> it ;
//内部类型:iterator_category代表迭代器类别类型
cout << typeid(iterator<input_iterator_tag, int>::iterator_category).name() << endl;
//迭代器指向元素的类型
cout << typeid( iterator<input_iterator_tag, int>::value_type).name() << endl;
//迭代器指向元素的指针类型
cout << typeid(iterator<input_iterator_tag, int>::pointer).name() << endl;
//迭代器指向元素的引用类型
cout << typeid(iterator<input_iterator_tag, int>::reference).name() << endl;
//迭代器指向元素的指针类型 相减得到的差值类型
cout << typeid(iterator<input_iterator_tag, int>::difference_type).name() << endl;
iterator<input_iterator_tag, int>::value_type a = 100;
iterator<input_iterator_tag, int>::pointer pA = &a;
iterator<input_iterator_tag, int>::reference b=a;
cout << a << " " << *pA << endl;
b = 200;
cout << a<<" "<< b << endl;
iterator<input_iterator_tag, int>::pointer pB = &a+2;
iterator<input_iterator_tag, int>::difference_type c = pB - pA;
cout << c << endl;
//测试自定义的迭代器
int numbers[] = { 10,20,30,40,50 };
MyIterator from(numbers);
MyIterator until(numbers + 5);
for (MyIterator it = from; it != until; it++)
std::cout << *it << ' ';
std::cout << '\n';
//cout << from[3] << endl;
MyIterator::value_type x = 100;
return 0;
}
10. 序列容器对比
序列容器 | 数据结构 | 底层实现 | 迭代器 | 优缺点 |
---|---|---|---|---|
array (c++11) | 固定数组 | 普通数组、连续内存 | 随机访问迭代器 | 快速随机访问、在中间插入、删除元素效率较低、无法扩容 |
vector | 动态数组 | new 堆内存 、连续内存 | 随机访问迭代器 | 快速随机访问、在中间插入、删除元素效率较低、容量可动态增长 |
deque | 双端队列 | 一个中央控制器和多个缓冲区、不连续 | 随机访问迭代器 | 快速随机访问、两端插入、删除效率高 |
list | 双向链表 | 双向链表、不连续 | 双向迭代器 | 不支持随机访问、可双向遍历、任意位置插入、删除元素效率高 |
forward_list (c++11) | 单向链表 | 单向链表、不连续 | 前向迭代器 | 不支持随机访问、单向遍历、任意位置插入、删除元素效率高 |
11. 容器适配器对比
容器适配器 | 数据结构 | 底层实现 | 迭代器 | 特点 |
---|---|---|---|---|
stack | 栈 | 底层默认用queue 实现,也可以是vector 、list | 不支持 | 先进后出 FILO |
queue | 队列 | 底层默认用queue 实现,也可以是list | 不支持 | 先进先出 FIFO |
priority_queue | 优先队列 | 底层默认用vector 为容器,使用堆heap 为处理规则来排序。容器也可以是deque | 不支持 | 优先级最高(或最低)的元素先出 |
12. 关联容器对比
序列容器 | 数据结构 | 底层实现 | 迭代器 | 优缺点 |
---|---|---|---|---|
set | 集合 | 红黑树 | 双向迭代器 | key唯一、有序 |
map | 映射 | 红黑树 | 双向迭代器 | key唯一、有序、value可重复 |
multiset | 多重集合 | 红黑树 | 双向迭代器 | key可重复、有序 |
multimap | 多重映射 | 红黑树 | 双向迭代器 | key可重复、有序、value可重复 |
unordered_set | 无序集合 | 哈希表 | 双向迭代器 | key唯一、无序 |
unordered_map | 无序映射 | 哈希表 | 双向迭代器 | key唯一、无序、value可重复 |
unordered_multiset (c++11) | 无序多重集合 | 哈希表 | 双向迭代器 | key可重复、无序 |
unordered_multimap (c++11) | 无序多重映射 | 哈希表 | 双向迭代器 | key可重复、无序、value可重复 |