STL中的迭代器模式:将算法与数据结构分离

目录

1.概述

2.容器类

2.1.序列容器

2.2.关联容器

2.3.容器适配器

2.4.数组

3.迭代器

4.重用标准迭代器

5.总结


1.概述

在之前,我们讲了迭代器设计模式,分析了它的结构、角色以及优缺点:

设计模式之迭代器模式-CSDN博客

在 STL 中,算法通常作为函数模板来实现,而数据结构(容器)则是模板类。这种分离使得算法可以独立于特定的数据结构来编写和使用,只要数据结构满足算法所需的接口(例如迭代器接口)。

STL的中心思想是将算法与数据结构分离,彼此独立设计,最后在用iterator将他们结合在一起,获得最大的适配性。通过"迭代器"接口,已经实现了将算法与数据结构分离的目的。

2.容器类

容器类是STL非常重要的一部分,它们提供了用于存储、检索和操作数据对象(如整数、浮点数、字符串等)的模板类。以下是STL中一些主要的容器类:

2.1.序列容器

这些容器中的元素可以看作是线性排列的。

std::vector: 动态数组,可以动态地增加和减少元素。

std::deque: 双端队列,支持在序列的开头和结尾处快速插入和删除元素。

std::list: 双向链表,允许在任何位置进行快速插入和删除操作。

std::forward_list: 单向链表,仅支持向前迭代,并且在序列的开头进行快速插入和删除操作。

std::array: 固定大小的数组,不是动态的,但提供了更好的性能。

std::string: 特殊的动态字符数组,用于存储字符串。

std::stack: 后入先出(LIFO)的容器适配器,通常基于std::deque或std::vector实现。

std::queue: 先入先出(FIFO)的容器适配器,通常基于std::deque或std::list实现。

std::initializer_list: 初始化其它容器的容器,只是std::initializer_list不存储数据,详见:

C++之std::initializer_list详解_c++ std initializer-CSDN博客

2.2.关联容器

这些容器中的元素是按键(key)存储的,并且按键进行排序。

std::set: 集合,包含唯一的元素,按键排序。

**std::multiset:**允许包含重复元素的集合。

std::map: 关联数组,其中每个元素都是一对键/值对,按键排序。

std::multimap: 允许键重复的关联数组。

std::unordered_set: 无序集合,包含唯一的元素,但不按键排序。

std::unordered_multiset: 允许包含重复元素的无序集合。

std::unordered_map: 无序关联数组,其中每个元素都是一对键/值对,但不按键排序。

std::unordered_multimap: 允许键重复的无序关联数组。

2.3.容器适配器

这些类不是容器本身,但它们为其他容器类提供了不同的接口。

std::stack: 后入先出(LIFO)的容器适配器。

std::queue: 先入先出(FIFO)的容器适配器。

**std::priority_queue:**优先级队列,其中元素按优先级排序(通常基于堆实现)。

2.4.数组

对于C数组,指针扮演迭代器的角色。

cpp 复制代码
int myInts[100];
std::for_each(myInts, myInts + 100, doSomething);

严格来说,myInts不是指针而是数组,但它仍然提供对数组的第一个元素的访问,而myInts + 100指向"结束后"的地址,符合begin-end的语义。

因此,C数组可以与算法一起使用,在旧代码中非常有帮助。

需要注意的是,自C++11以来引入了一种新的统一语法,使用std::begin(和std::end)自由函数(而不是类方法)。它们可以统一地用于任何具有可以无参数调用的begin(或end)方法的类型,并且也可以用于C数组。

下面的代码示例说明了这种统一性:

cpp 复制代码
int myInts[100];
std::vector<int> vec(100, 0); // 大小为100且初始化为0

std::for_each(std::begin(vec), std::end(vec), doSomething);
std::for_each(std::begin(myInts), std::end(myInts), doSomething);

这使得使用C数组变得更加简单,并且对于通用代码非常方便。

需要注意的是,对于C数组,必须显式地写出std命名空间,因为它无法使用ADL,但对于vector可以省略std命名空间。

3.迭代器

  1. 上述序列容器关联容器都提供了标准的迭代器操作:

正向迭代器:begin()和end()

反向迭代器:rbegin()和rend()

常量正向迭代器:cbegin()和cend()(或者const容器上的begin()和end())

常量反向迭代器:crbegin()和crend()(或者const容器上的rbegin()和rend())

  1. 容器迭代器是不提供这些操作的。

  2. 数组可以用std::begin()和std::end()来获取正向迭代器、反向迭代器、常量正向迭代器和常量反向迭代器。

4.重用标准迭代器

如果集合确实需要领域功能,或者只想要标准容器提供的部分功能,可能需要定义一个包装标准容器的类。在这种情况下,可以使用标准容器的迭代器来实现迭代器:

cpp 复制代码
// 接口
class FlowCollection
{
public:
    // ...领域接口...

    // 允许访问数据的迭代器
    using const_iterator = std::vector<Flow>::const_iterator;
    const_iterator begin() const;
    const_iterator end() const;

    // 允许修改数据的迭代器
    using iterator = std::vector<Flow>::iterator;
    iterator begin();
    iterator end();

    // 其他迭代器...

private:
    std::vector<Flow> m_flows;
    // ...领域数据...
};


// 实现
FlowCollection::iterator FlowCollection::begin()
{
    return m_flows.begin();
}

那么算法就可以这样调用,以排序为例:

cpp 复制代码
FlowCollection  data;

std::sort(data.begin(), data.end());

5.总结

STL 中的算法是一组函数模板,它们接受迭代器作为参数,并对迭代器范围内的元素执行操作。这些算法包括排序、搜索、复制、删除等操作。由于算法是独立于数据结构的,因此它们可以与任何支持迭代器接口的容器一起使用。

通过将算法与容器分离,STL 允许程序员根据需要组合不同的算法和容器。例如,你可以使用排序算法对向量进行排序,或者使用搜索算法在集合中查找元素。这种灵活性使得 STL 能够适应各种不同的应用场景。

总的来说,STL 的设计方式通过将算法与数据结构分离,实现了代码的可重用性、可维护性和灵活性。这使得 STL 成为 C++ 程序员在处理数据结构和算法时的重要工具。

相关推荐
LeoLei80603 分钟前
新特性之C++17
开发语言·c++
码农超哥同学12 分钟前
Python面试题:如何在 Python 中反转一个字符串?
开发语言·windows·python·面试·编程
Ezekiel Mok23 分钟前
基于路径长度的样条插补算法(自动驾驶和路径跟踪控制适用)
算法·matlab·自动驾驶·路径跟踪·插补
不死鸟.亚历山大.狼崽子30 分钟前
python库(2):Passlib库
开发语言·python
jllws133 分钟前
C++基础:STL概述
开发语言·c++
FightingLod36 分钟前
C++中list容器使用详解
开发语言·c++·list
yyqzjw42 分钟前
【C++】单例模式
c++·单例模式
Zucker n1 小时前
学会python——用python制作一个登录和注册窗口(python实例十八)
开发语言·python
mana飞侠1 小时前
代码随想录算法训练营第59天:动态[1]
开发语言·数据结构·算法·动态规划
每天的积累1 小时前
C++学习笔记二
c++·笔记·学习