c++专题-标准模板库STL
- 1 标准模板库概述
- 2 序列式容器
- 2.1 vector 容器
- 2.2 deque 容器
- 2.3 list 容器
- 3 关联式容器
- 4 无序关联容器
- 5 容器适配器
- 5.1 STL容器适配器的种类
- 5.2 stack容器适配器
- 5.3 queue容器适配器
- 5.3 priority_queue容器适配器
1 标准模板库概述
STL,英文全称 standard template library,中文可译为标准模板库或者泛型库,其包含有大量的模板类和模板函数,是 C++ 提供的一个基础模板的集合,用于完成诸如输入/输出、数学计算等功能
容器的分类
2 序列式容器
2.1 vector 容器
2.2 deque 容器
2.3 list 容器
STL list 容器,又称双向链表容器,即该容器的底层是以双向链表的形式实现的。这意味着,list 容器中的元素可以分散存储在内存空间里,而不是必须存储在一整块连续的内存空间中 st 容器中各个元素的前后顺序是靠指针来维系的,每个元素都配备了 2 个指针,分别指向它的前一个元素和后一个元素。其中第一个元素的前向指针总为 null,因为它前面没有元素;同样,尾部元素的后向指针也总为 null。
基于这样的存储结构,list 容器具有一些其它容器(array、vector 和 deque)所不具备的优势,即它可以在序列已知的任何位置快速插入或删除元素(时间复杂度为O(1))。并且在 list 容器中移动元素,也比其它容器的效率高
- list 容器的创建
/**
*
* list 相关的方法测试
* @brief AdapterTest::listTest
* @return
*/
int32 AdapterTest::listTest()
{
// 创建一个没有任何元素的空list容器
std::list<int32> li1;
// 创建一个包含n个元素的list容器
std::list<int32> li2(10);
// 创建一个包含n个元素的list容器,并为每个元素指定初始值
std::list<int32> li3(10,8);
// 在已有 list 容器的情况下,通过拷贝该容器可以创建新的list容器
std::list<int32> li4(li3);// 采用此方式,必须保证新旧容器存储的元素类型一致
std::list<int32> testList;
for(int i = 0; i < 100;i+=10){
if ((i & 1) == 0){
testList.push_back(i);
}
}
cout << "size : " << testList.size() << endl;
// begin() 返回指向容器中第一个元素的双向迭代器
// end() 返回指向容器中最后一个元素所在位置的下一个位置的双向迭代器
std::list<int32>::iterator iter = testList.begin();
// 1.遍历
cout << "------using iterator to travelsal list begin------" << endl;
for(;iter != testList.end();++iter)
{
cout << *iter << " ";
}
cout << endl;
cout << "------using iterator to travelsal list end------" << endl;
// const_iterator cbegin() 也可以换成 begin() cend() 也可以换成 end()
std::list<int32>::const_iterator citer = testList.cbegin();
cout << "------using const_iterator to travelsal list begin------" << endl;
for(;citer != testList.cend();++citer)
{
cout << *citer << " ";
}
cout << endl;
cout << "------using const_iterator to travelsal list end------" << endl;
// 反向迭代器 rbegin() rend()
cout << "------using reverse_iterator to travelsal list begin------" << endl;
// std::list<int32>::const_reverse_iterator 使用这个也可以
std::list<int32>::reverse_iterator riter = testList.rbegin();
for(;riter != testList.rend();++ riter)
{
cout << *riter << " ";
}
cout << endl;
cout << "------using reverse_iterator to travelsal list end------" << endl;
cout << "empty : " << testList.empty() << endl;
cout << "size : " << testList.size() << endl;
// 返回第一个元素的引用
cout << "front : " << testList.front() << endl;
// 返回最后一个元素的引用
cout << "back : " << testList.back() << endl;
// 在容器尾部插入一个元素
testList.push_back(766);
cout << "back : " << testList.back() << endl;
// 删除容器尾部的一个元素 无返回值
testList.pop_back();
// 在容器中的指定位置插入元素。该函数和insert()功能相同,但效率更高
testList.emplace(testList.end(),800);
cout << "back : " << testList.back() << endl;
// insert() 在容器中的指定位置插入元素
testList.insert(testList.begin(),900);
cout << "front : " << testList.front() << endl;
// 删除指定区间的元素 返回指向下一个元素的迭代器
iter = testList.erase(testList.begin());
cout << *iter << endl;
}
- list 遍历删除元素
int32 AdapterTest::listRemoveTest()
{
std::list<int32> testList;
for(int i = 0;i<100;++i){
testList.push_back(i);
}
std::list<int32>::const_iterator iter = testList.begin();
for (;iter != testList.end();++iter)
{
cout << *iter << " ";
}
cout << endl;
for(iter = testList.begin();iter != testList.end();)
{
if((*iter & 1) == 0)
{
// 删除 iter 指向的元素 并返回指向下一个元素的迭代器
iter = testList.erase(iter);
} else {
++iter;
}
}
cout << "------------------------------" << endl;
iter = testList.begin();
for (;iter != testList.end();++iter)
{
cout << *iter << " ";
}
cout << endl;
}
3 关联式容器
4 无序关联容器
5 容器适配器
5.1 STL容器适配器的种类
公共的头文件部分commondef.h
#ifndef COMMONDEF_H
#define COMMONDEF_H
#include <iostream>
#include <array>
#include <vector>
#include <string>
#include <iostream>
#include <algorithm>
#include <map>
#include <utility> // pair 类模板定义在<utility>头文件中
#include <set>
#include <cstring>
#include <typeinfo>
#include <deque>
#include <list>
#include <forward_list>
#define TABLE_NUM 14
#define AISTD std::
#define TABLE_NAME_LEN 128
const char PATH_SEPARATOR = '/';
#define DATE_CHANGE_NUM 1000000LL
#define BUFFER_SIZE 1024
#define LINE cout << "********************************************************" << endl;
#define LINEFEED cout << endl;
#define FUNCTION_BEGIN \
cout << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << " : begin >>> " << endl;
#define FUNCTION_END \
cout << __FILE__ << ":" << __LINE__ << ":" << __FUNCTION__ << " : end >>> " << endl;
typedef std::string aistring;
typedef int int32;
typedef long long int64;
typedef short int int16;
using namespace std;
#endif // COMMONDEF_H
简单的理解容器适配器,其就是将不适用的序列式容器(包括 vector、deque 和 list)变得适用。容器适配器的底层实现和模板 A、B 的关系是完全相同的,即通过封装某个序列式容器,并重新组合该容器中包含的成员函数,使其满足某些特定场景的需要。
STL 提供了 3 种容器适配器,分别为 stack
栈适配器、queue
队列适配器以及 priority_queue
优先权队列适配器。其中,各适配器所使用的默认基础容器以及可供用户选择的基础容器
容器适配器 | 基础容器筛选条件 | 默认使用的基础容器 |
---|---|---|
stack | 基础容器需包含以下成员函数: empty() size() back() push_back() pop_back() 满足条件的基础容器有 vector、deque、list | deque |
queue | 基础容器需包含以下成员函数: empty() size() front() back() push_back() pop_front() 满足条件的基础容器有 deque、list | deque |
priority_queue | 基础容器需包含以下成员函数: empty() size() front() push_back() pop_back() 满足条件的基础容器有vector、deque | vector |
5.2 stack容器适配器
stack容器适配器的创建
/**
*
* stack使用的基础容器有 :
* - vector
* - list
* - deque (默认)
* @brief AdapterTest::stackTest
* @return
*
*/
int32 AdapterTest::stackTest()
{
// 创建一个不包含任何元素的 stack 适配器,并采用默认的 deque 基础容器
stack<int32> s1;
// 使用基础容器list来初始化 stack 适配器,只要该容器的类型和 stack 底层使用的基础容器类型相同即可
stack<int32,list<int32>> s2;
for(int i = 0;i < 10;++i)
{
// 压栈
s1.push(i);
}
if(!s1.empty())
{
// 栈顶元素
cout << "top : " << s1.top() << endl;
// 栈中元素的个数
cout << "size : " << s1.size() << endl;
}
while(!s1.empty()){
std::cout << s1.top() << " ";
s1.pop();
}
std::cout << endl;
return 0;
}
5.3 queue容器适配器
和 stack
栈容器适配器不同,queue
容器适配器有 2 个开口,其中一个开口专门用来输入数据,另一个专门用来输出数据
最先进入 queue
的元素,也可以最先从 queue
中出来,即用此容器适配器存储数据具有“先进先出(简称 “FIFO” )”的特点,因此 queue
又称为队列适配器。
queue适配器的创建及常用成员函数
int32 AdapterTest::queueTest()
{
// 创建一个空的 queue 容器适配器,其底层使用的基础容器选择默认的 deque 容器
queue<int32> q1;
// 创建了一个使用 list 容器作为基础容器的空 queue 容器适配器
queue<int32,list<int32>> q2;
// 用基础容器来初始化 queue 容器适配器,只要该容器类型和 queue 底层使用的基础容器类型相同即可
std::deque<int32> values{1,6,7,10,12};
queue<int32> q3(values);
/**
* queue支持的成员函数
* - empty()
* - front() 返回queue中第一个元素的引用.如果queue是常量,就返回一个常引用;如果queue为空,返回值是未定义的
* - back() 返回queue中最后一个元素的引用.如果queue是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的
* - push() 在queue的尾部添加一个元素的副本.这是通过调用底层容器的成员函数push_back()来完成的.
* - pop() 删除 queue 中的第一个元素
*/
cout << "empty : " << q3.empty() << endl;
int32& val = q3.front();
cout << "val : " << val << endl;
val = q3.back();
cout << "val : " << val << endl;
q3.push(20);
val = q3.back();
cout << "val : " << val << endl;
while (! q3.empty())
{
cout << "front ele " << q3.front() << endl;
q3.pop();
}
return 0;
}
5.3 priority_queue容器适配器
priority_queue 容器适配器模拟的也是队列这种存储结构,即使用此容器适配器存储元素只能“从一端进(称为队尾),从另一端出(称为队头)”,且每次只能访问 priority_queue 中位于队头的元素
但是,priority_queue 容器适配器中元素的存和取,遵循的并不是 “First in,First out”(先入先出)原则,而是"First in,Largest out"原则。直白的翻译,指的就是先进队列的元素并不一定先出队列,而是优先级最大的元素最先出队列
priority_queue 容器适配器为了保证每次从队头移除的都是当前优先级最高的元素,每当有新元素进入,它都会根据既定的排序规则找到优先级最高的元素,并将其移动到队列的队头;同样,当 priority_queue 从队头移除出一个元素之后,它也会再找到当前优先级最高的元素,并将其移动到队头
基于 priority_queue 的这种特性,因此该容器适配器有被称为优先级队列
template <typename T,
typename Container=std::vector<T>,
typename Compare=std::less<T> >
class priority_queue{
//......
}
typename T
:指定存储元素的具体类型;
typename Container
:指定 priority_queue
底层使用的基础容器,默认使用 vector
容器
typename Compare
:指定容器中评定元素优先级所遵循的排序规则,默认使用std::less<T>
按照元素值从大到小进行排序,还可以使用std::greater<T>
按照元素值从小到大排序,但更多情况下是使用自定义的排序规则
- priority_queue创建的几种方式
由于 priority_queue 容器适配器模板位于头文件中,并定义在 std 命名空间里,因此在试图创建该类型容器之前,程序中需包含以下 2 行代码:
#include <queue>
using namespace std;
/**
* priority_queue 使用的基础容器
* - vector (默认)
* - dequeu
* @brief AdapterTest::priorityQueueTest
* @return
*/
int32 AdapterTest::priorityQueueTest()
{
// 创建一个空的priority_queue容器适配器,底层采用默认的vector容器,排序方式也采用默认的std::less<T>方法
std::priority_queue<int32> pq;
// 可以使用普通数组或其它容器中指定范围内的数据,对priority_queue容器适配器进行初始化:
int values[]{20,30,90,17,23,45};//使用普通数组
std::priority_queue<int32> q1(values,values+5);
std::vector<int32> v1{100,78,32,87,49,23};//使用向量
std::priority_queue<int32> q2(v1.begin(),v1.end());
// 指定优先级队列的底层排序规则 小的排在前面
std::priority_queue<int32,std::deque<int32>,std::greater<int32>> q3(v1.begin(),v1.end());
/**
* priority_queue提供的成员函数
* - empty() 如果 priority_queue 为空的话,返回 true;反之,返回 false
* - size() 返回 priority_queue 中存储元素的个数
* - top() 返回 priority_queue 中第一个元素的引用形式
* - push(const T& obj)
* 根据既定的排序规则,将元素 obj 的副本存储到 priority_queue 中适当的位置。
* - push(T&& obj)
* 根据既定的排序规则,将元素 obj 移动存储到 priority_queue 中适当的位置
* - pop()
* 移除 priority_queue 容器适配器中第一个元素
* - swap(priority_queue<T>& other)
* 将两个priority_queue容器适配器中的元素进行互换,
* 需要注意的是,进行互换的2个priority_queue容器
* 适配器中存储的元素类型以及底层采用的基础容器类型
* 都必须相同
*/
cout << "size : " << q3.size() << endl;
while (!q3.empty())
{
cout << q3.top() << " ";
q3.pop();
}
cout << endl;
cout << "---------------------q2---------------------" << endl;
cout << "size : " << q2.size() << endl;
while (!q2.empty())
{
cout << q2.top() << " ";
q2.pop();
}
cout << endl;
int32 nums[]{10,34,56,89,78,20};
std::priority_queue<int32> q4(nums,nums+5);
q2.swap(q4);
cout << "---------------------q2---------------------" << endl;
while (!q2.empty())
{
cout << q2.top() << " ";
q2.pop();
}
cout << endl;
return 0;
}
- priority_queue容器适配器实现自定义排序
无论 priority_queue 中存储的是基础数据类型(int、double 等),还是 string 类对象或者自定义的类对象,都可以使用函数对象的方式自定义排序规则
template <typename T>
// 函数对象类
class IntegerComp
{
public:
IntegerComp() {}
//重载()
bool operator()(const T& l,const T& r)
{
return l < r;
}
};
上面的 class
关键字也可以使用 struct
关键字替代
// 函数对象类
template <typename T>
struct IntegerComp
{
public:
IntegerComp() {}
//重载()
bool operator()(const T& l,const T& r)
{
return l > r;
}
};
自定义优先级 coding
/**
*
*
* 优先级队列自定义排序规则
* @brief AdapterTest::priorityQueueCustomerTest
* @return
*/
int32 AdapterTest::priorityQueueCustomerTest()
{
int values[]{10,6,23,78,87,90};
// l < r 时升序
// l > r 时降序
std::priority_queue<int32,std::deque<int32>,IntegerComp<int32>> prioQueue(values,values + 6);
cout << "------------prioQueue: ------------" << endl;
while(! prioQueue.empty())
{
cout << prioQueue.top() << " ";
prioQueue.pop();
}
cout << endl;
}