【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++
【C++】优先级队列(容器适配器)
本文介绍了C++ STL中的线性容器及其适配器,包括栈、队列和优先队列的设计与实现。详细解析了`deque`的特点和存储结构,以及如何利用`deque`实现栈、队列和优先队列。通过自定义命名空间和类模板,展示了如何模拟实现这些容器适配器,重点讲解了优先队列的内部机制,如堆的构建与维护方法。
42 0
|
3月前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
69 2
|
3月前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
85 2
|
2月前
|
存储 编译器 C语言
【c++丨STL】string类的使用
本文介绍了C++中`string`类的基本概念及其主要接口。`string`类在C++标准库中扮演着重要角色,它提供了比C语言中字符串处理函数更丰富、安全和便捷的功能。文章详细讲解了`string`类的构造函数、赋值运算符、容量管理接口、元素访问及遍历方法、字符串修改操作、字符串运算接口、常量成员和非成员函数等内容。通过实例演示了如何使用这些接口进行字符串的创建、修改、查找和比较等操作,帮助读者更好地理解和掌握`string`类的应用。
66 2
|
2月前
|
存储 编译器 C++
【c++】类和对象(下)(取地址运算符重载、深究构造函数、类型转换、static修饰成员、友元、内部类、匿名对象)
本文介绍了C++中类和对象的高级特性,包括取地址运算符重载、构造函数的初始化列表、类型转换、static修饰成员、友元、内部类及匿名对象等内容。文章详细解释了每个概念的使用方法和注意事项,帮助读者深入了解C++面向对象编程的核心机制。
118 5
|
2月前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
120 4
|
2月前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
160 4
|
3月前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
36 4
|
3月前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
34 4
|
3月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
33 1

热门文章

最新文章