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;
}