【C++ STL】容器适配器(Stack & Queue & Priotity_Queue)-- 详解(下)

简介: 【C++ STL】容器适配器(Stack & Queue & Priotity_Queue)-- 详解(下)

【C++ STL】容器适配器(Stack & Queue & Priotity_Queue)-- 详解(上)https://developer.aliyun.com/article/1514695?spm=a2c6h.13148508.setting.23.4b904f0ejdbHoA

🔺4、仿函数

(1)什么是仿函数

仿函数(Functor)又称为函数对象(Function Object),使一个类的使用看上去像一个函数,其实就是在类中重载了 operator() 运算符,这个类就有了类似函数的行为,就是一个仿函数类了。

仿函数的语法几乎和我们普通的函数调用一样,调用仿函数时,实际上就是通过仿函数类对象调用重载后的 operator() 运算符,这种行为类似函数调用。

// 仿函数(函数对象) --自定义类型
struct Less
{
  bool operator()(const int& x, const int& y) // 重载()运算符
  {
    return x < y;
  }
};
 
void test_functor()
{
  // 方式(1)
  Less less; // 构造函数对象
  cout << less(1, 2) << endl;  // 编译器会解释成:less.operator()(1, 2);
 
  // 方式(2)
  cout << Less()(1, 2) << endl; // 构造一个匿名函数对象
}

cplusplus.com/reference/functional/greater/

cplusplus.com/reference/functional/less/

仿函数类还可以写成类模板,适应更多的类型:

template<class T> // 用于小于(<)不等式比较的函数对象类
struct Less
{
  bool operator()(const T& x, const T& y)
  {
    return x < y;
  }
};
 
template<class T> // 用于大于(>)不等式比较的函数对象类
struct Greater
{
  bool operator()(const T& x, const T& y)
  {
    return x > y;
  }
};
 
void test_functor()
{
  Less<int> less;
  cout << less(1, 2) << endl; // true
  
    Greater<int> greater;
  cout << greater(1, 2) << endl; // false
}

仿函数 less 和 greater 是继承的 binary_function,可以看作是对于一类函数的总体声明,这是函数做不到的。

template <class T> struct greater : binary_function <T, T, bool>
{
    bool operator() (const T& x, const T& y) const
    {
        return x > y;
    }
};
 
template <class T> struct less : binary_function <T, T, bool>
{
    bool operator() (const T& x, const T& y) const
    {
        return x < y;
    }
};

(2)模板实例化时,仿函数的使用

类模板一般是显式实例化的,在 <> 中指定模板参数的实际类型,所以类模板是传类型。比如: priority_queue。

//第1个模板参数是:存储数据的类型
//第2个模板参数是:基础容器的类型
//第3个模板参数是:仿函数的类型
template <class T, class Container = vector<T>,
    class Compare = less<typename Container::value_type>> class priority_queue;
 
void test()
{
    // 建小堆
    priority_queue<int, vector<int>, greater<int>> pq; // 传仿函数greater<int>类型
}

而函数模板一般是隐式实例化,让编译器根据实参推演模板参数的实际类型,所以函数模板是传对象。比如:sort。

// 第1个模板参数:迭代器的类型
// 第2个模板参数是:仿函数的类型
template <class RandomAccessIterator, class Compare>
 
// 函数的第1、2个参数是:迭代器对象
// 函数的第3个参数是:仿函数类的对象
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
 
void test()
{
    vector<int> v { 5,3,2,4,1 };
    // 排降序(>)
    sort (v.begin(), b.end(), greater<int>()); // 传仿函数类greater<int>的匿名对象
    for (const auto& x : v)
        cout << x << " ";
    cout << endl;
}

四、容器适配器

stack 和 queue 和 priority_queue 往往不被认为是一个容器,而是一个容器适配。

adapter 原意是插座、适配器、接合器的意思。


1、什么是适配器

适配器 是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口。


2、STL 标准库中 stack queue 的底层结构

虽然 stack 和 queue 中也可以存放元素,但在 STL 中并没有将其划分在容器的行列,而是将其称为容器适配器,这是因为 stack 和 queue 只是对其他容器的接口进行了包装,STL 中 stack 和 queue 默认使用 deque priority_queue 默认使用 vector


3、deque的简单介绍(了解)

cplusplus.com/reference/deque/deque/


(1)deque 的原理介绍

deque( 双端队列 ):是一种双开口的 “连续” 空间的数据结构。

双开口的含义是:可以在头尾两端进行插入和删除操作,且 时间复杂度为 O(1) 与 vector 比较,头插效率高,不需要搬移元素;与 list 比较,空间利用率比较高。

  • vector 是一段连续的物理空间

优点

  1. 支持随机访问
  2. 空间利用率高,底层是连续空间,不容易造成内存碎片。
  3. CPU 高速缓存命中率很高。

缺点

  1. 空间不够时需要增容,增容代价很大(需要经过重新配置空间、元素搬移、释放原空间等),同时还存在一定的空间浪费。
  2. 头部和中间插入删除,效率很低 O(n)。

  • list 不是连续的物理空间,而是由一个个节点 “链接” 起来的

优点

  1. 按需申请释放空间,不会浪费空间
  2. 任意位置插入和删除数据都是 O(1),因为不需要移动数据,插入删除效率高

缺点

  1. 不支持随机访问
  2. 空间利用率低,底层不是连续的空间,小节点容易造成内存碎片。
  3. CPU 高速缓存命中率很低。

  • deque 并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际 deque 类似于一个动态的二维数组。
  1. deque 支持很多操作,比如 vector 不支持头插头删(因为效率太低),而 deque 支持。
  2. list 不支持随机访问,而 deque 支持。

起来就像完美融合了 vector 和 list 的操作。

当需要增容时,只需要经过重新配置空间、元素搬移、释放原空间等过程,而是新增一个 buffer,存入数据,然后让中控数组指向新增的 buffer,将其管理起来。

deque 底层是一段假象的连续空间,实际是分段连续的,为了维护其 “整体连续” 以及随机访问的假象,落在了 deque 的迭代器身上,因此 deque 的迭代器设计就比较复杂(包含4个指针) ,如下图所示:(deque 的中控器、缓冲区、迭代器的相互关系:


(2)deque 的优点和缺陷

与 vector 比较,deque 的 优势 是:

  • 头部插入和删除时,不需要搬移元素,效率特别高。
  • 扩容时,也不需要搬移大量的元素,因此其效率是比 vector 高的。

与 list 比较,其底层是连续空间空间利用率比较高,不需要存储额外字段。

但是, deque 有一个 致命缺陷

  • 不适合遍历,因为在遍历时,deque 的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑 vector 和 list,deque 的应用并不多,而目前能看到的一个应用就是,STL 用其作为 stack 和 queue 的底层数据结构
  • 同时,deque 在中间插入删除数据,非常麻烦,效率很低。
deque 是一种折中方案的(妥协)设计,不够极致,随机访问效率不及 vector,任意位置插入删除不及 list,所以它能替代 vector 和 list 吗?

不能。


4、为什么选择deque作为stackqueue的底层默认容器

  • stack 是一种后进先出的特殊线性数据结构,因此只要具有 push_back() 和 pop_back() 操作的线性结构,都可以作为 stack 的底层容器,比如 vector 和 list 都可以;
  • queue 是先进先出的特殊线性数据结构,只要具有 push_back() 和 pop_front() 操作的线性结构,都可以作为 queue 的底层容器,比如 list。

但是 STL 中对 stack 和 queue 默认选择 deque 作为其底层容器,主要是因为:
  1. stack 和 queue 不需要遍历(因此 stack 和 queue 没有迭代器),只需要在固定的一端或者两端进行操作。
  2. 在 stack 中元素增长时,deque 比 vector 的效率高(扩容时不需要搬移大量数据)。
  3. queue 中的元素增长时,deque 不仅效率高,而且内存使用率高。


相关文章
|
2天前
|
存储 调度 C++
|
3天前
|
存储 算法 C语言
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
【C++】详解STL的适配器容器之一:优先级队列 priority_queue
|
3天前
|
设计模式 存储 缓存
【C++】详解STL容器之一的deque和适配器stack,queue
【C++】详解STL容器之一的deque和适配器stack,queue
|
18天前
|
NoSQL 关系型数据库 Redis
Docker的通俗理解和通过宿主机端口访问Redis容器的实例
本文目标:引导初学者入门Docker,理解镜像、容器和宿主机概念,学习常用Docker命令,特别是如何创建并从Redis容器通过宿主机端口访问。 关键点: - Docker核心:镜像(类)、容器(实例)、宿主机(运行环境)。 - `docker pull` 拉取镜像,如 `redis:3.0`。 - `docker run -d --name` 后台运行容器,如 `my-redis`。 - `-p` 参数做端口映射,如 `6379:6379`。 - `docker exec -it` 交互式进入容器,如 `bash` 或执行命令。
|
15天前
|
前端开发 安全 数据库
Web架构&前后端分离站&Docker容器站&集成软件站&建站分配
Web架构&前后端分离站&Docker容器站&集成软件站&建站分配
|
2天前
|
安全 关系型数据库 开发者
Docker Compose凭借其简单易用的特性,已经成为开发者在构建和管理多容器应用时不可或缺的工具。
Docker Compose是容器编排利器,简化多容器应用管理。通过YAML文件定义服务、网络和卷,一键启动应用环境。核心概念包括服务(组件集合)、网络(灵活通信)、卷(数据持久化)。实战中,编写docker-compose.yml,如设置Nginx和Postgres服务,用`docker-compose up -d`启动。高级特性涉及依赖、环境变量、健康检查和数据持久化。最佳实践涵盖环境隔离、CI/CD、资源管理和安全措施。案例分析展示如何构建微服务应用栈,实现一键部署。Docker Compose助力开发者高效驾驭复杂容器场景。
12 1
|
3天前
|
存储 监控 安全
Docker Compose:轻松实现容器编排的利器
【7月更文挑战第2天】 1. **基础与概念**:服务(多容器实例)、网络(灵活通信)、卷(数据持久化)和配置(安全管理)。 2. **实战指南**:安装Compose,编写`docker-compose.yml`文件,启动应用,并介绍依赖、环境变量、健康检查和数据持久化。 3. **最佳实践**:环境隔离、CI/CD集成、资源管理、日志监控、安全策略及案例分析,展示完整应用栈搭建。
13 1
|
12天前
|
NoSQL Redis Docker
使用 Docker Compose 接管现有容器的文档
使用 Docker Compose 接管现有容器的文档
25 2
|
15天前
|
Cloud Native 安全 Docker
云上攻防-云原生篇&Docker安全&系统内核&版本&CDK自动利用&容器逃逸
云上攻防-云原生篇&Docker安全&系统内核&版本&CDK自动利用&容器逃逸
|
12天前
|
存储 关系型数据库 MySQL
解读 MySQL 容器信息:`docker inspect` 字段详解
解读 MySQL 容器信息:`docker inspect` 字段详解
29 1