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数组,指针扮演迭代器的角色。

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数组。

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

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())

2) 容器迭代器是不提供这些操作的。

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

4.重用标准迭代器

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

// 接口
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();
}

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

FlowCollection  data;

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

5.总结

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

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

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

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值