C++ STL中list迭代器的实现

简介: C++ STL中list迭代器的实现

list 的模拟实现中,重难点在于迭代器功能的实现,因此本文只围绕 iterator 及 const_iterator 的设计进行介绍,其余如增删查改则不再赘述——在C语言的基础上,这些都非常简单。


与 string / vector 不同,list 的节点原生指针不能通过简单的 ++ / * 等实现迭代器,因此我们需要对节点指针进行封装,利用自定义类型支持运算符重载的性质,完成迭代器的设计。


为了与 STL中list 进行区分,我们在创建的命名空间内实现。


一、iterator 的实现

(双向带头循环)链表及其节点的结构设计:

push_back():

void push_back(const T& x)
{
    Node* newnode = new Node(x);
    Node* tail = _head->prev;

    tail->_next = newnode;
    newnode->_prev = tail;
    _head->_prev = newnode;
    newnode->_next = _head;
}

我们很容易可以插入一组数据,但如果想遍历链表,同时对数据进行修改,则需要实现迭代器。

list 无法像 vector 一样简单地 ++/* 实现节点遍历,且原始指针也不支持对运算符重载,我们需要对 list节点的指针 进行封装

template<class T>
struct __list_iterator
{
    typedef ListNode<T> Node;
    Node* _node;// 被封装的节点指针
    
    // 迭代器的构造
    __list_iterator(Node* node)
      :_node(node)
  {}
};

struct __list_iterator
{
  typedef __list_iterator<T> self;  
    
    self operator++()
    {
        _node = _node->_next;
        return _node;
    }
    
    T& operator*()
    {
        return _node->_data;
    }
};

实际上,编译器优化后,会直接用 _node 对等式左边的变量进行构造。

以此类推,后置++ / 前后置-- / != / == 等都很容易了。

二、const_iterator 及第二个模板参数的引入

const_iterator 不能通过对 iterator 加 const 实现:


const_iterator 是为了防止 list 节点的数据被修改;iterator 加 const 会让迭代器本身无法++/–,导致 const list 无法实现遍历——迭代器不能++/–

const_iterator 本身的功能很简单——防止 list 节点的数据被修改,我们只需要针对性的修改 * 重载,其余与普通迭代器没有区别。

const T& operator*()
{
    return _node->_data;
}
// 普通迭代器 iterator:
// T& operator*() —— 能对节点的数据进行修改

即:

template<class T>
struct __list_const_iterator
{
    // ...
  const T& operator*()
    {
        return _node->_data;
    }
};

template<class T>
class list
{
    // ...
    typedef __list_const_iterator<T> const_iterator;
}

仅有一个函数不同,而其余部分都一样(与 iterator 相比),这种做法不是很好。

因此,我们引入第二个模板参数。

template<class T, class Ref>
struct __list_iterator
{
    // ...
  Ref operator*()
    {
        return _node->_data;
    }
}

template<class T, class Ref>
class list
{
  typedef __list_iterator<T, T&> iterator;
    typedef __list_iterator<T, const T&> const_iterator;
    // ...
}
  • 用一个例子测试 const迭代器:
namespace MyList
{
    void Print(const list<int>& lt)
    {
        for (auto& e : lt)
        {
            cout << e << " ";
        }
        cout << endl;
    }
    void test1()
    {
        list<int> lt;
        lt.push_back(10);
        lt.push_back(20);
        lt.push_back(30);
        lt.push_back(40);
        
        Print(lt);
    }
}

int main()
{
    test1();
    return 0;
}

三、箭头->重载

T* operator->()
{
    return &(_node->_data);
}


观察一下:it->_year ——> it.operator->()_year

此处 it.operator->() 的返回值是 Date* ,不应该写成 it->->_year 吗?

事实上,编译器在此处进行了优化,->-> 的写法反而错了

我们也可以引入第三个模板参数 Ptr ,这就就可以同时实现 T* 和 const T* 。

template<class T, class Ref, class Ptr>
struct __list_iterator
{
    typedef __list_iterator<T, Ref, Ptr> self;
    // ...
};

template<class T>
class list
{
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;
    // ...
}
相关文章
|
4月前
|
缓存 算法 程序员
C++STL底层原理:探秘标准模板库的内部机制
🌟蒋星熠Jaxonic带你深入STL底层:从容器内存管理到红黑树、哈希表,剖析迭代器、算法与分配器核心机制,揭秘C++标准库的高效设计哲学与性能优化实践。
C++STL底层原理:探秘标准模板库的内部机制
|
11月前
|
编译器 C++ 容器
【c++丨STL】基于红黑树模拟实现set和map(附源码)
本文基于红黑树的实现,模拟了STL中的`set`和`map`容器。通过封装同一棵红黑树并进行适配修改,实现了两种容器的功能。主要步骤包括:1) 修改红黑树节点结构以支持不同数据类型;2) 使用仿函数适配键值比较逻辑;3) 实现双向迭代器支持遍历操作;4) 封装`insert`、`find`等接口,并为`map`实现`operator[]`。最终,通过测试代码验证了功能的正确性。此实现减少了代码冗余,展示了模板与仿函数的强大灵活性。
309 2
|
11月前
|
存储 算法 C++
【c++丨STL】map/multimap的使用
本文详细介绍了STL关联式容器中的`map`和`multimap`的使用方法。`map`基于红黑树实现,内部元素按键自动升序排列,存储键值对,支持通过键访问或修改值;而`multimap`允许存在重复键。文章从构造函数、迭代器、容量接口、元素访问接口、增删操作到其他操作接口全面解析了`map`的功能,并通过实例演示了如何用`map`统计字符串数组中各元素的出现次数。最后对比了`map`与`set`的区别,强调了`map`在处理键值关系时的优势。
607 73
|
11月前
|
存储 算法 C++
【c++丨STL】set/multiset的使用
本文深入解析了STL中的`set`和`multiset`容器,二者均为关联式容器,底层基于红黑树实现。`set`支持唯一性元素存储并自动排序,适用于高效查找场景;`multiset`允许重复元素。两者均具备O(logN)的插入、删除与查找复杂度。文章详细介绍了构造函数、迭代器、容量接口、增删操作(如`insert`、`erase`)、查找统计(如`find`、`count`)及`multiset`特有的区间操作(如`lower_bound`、`upper_bound`、`equal_range`)。最后预告了`map`容器的学习,其作为键值对存储的关联式容器,同样基于红黑树,具有高效操作特性。
493 3
|
12月前
|
存储 算法 C++
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
771 1
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
1472 1
|
运维 关系型数据库 Java
PolarDB产品使用问题之使用List或Range分区表时,Java代码是否需要进行改动
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
Java API Apache
怎么在在 Java 中对List进行分区
本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。
514 1
|
存储 安全 Java
详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法
详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法
306 3
|
Java API
使用 Java 来实现两个 List 的差集操作
使用 Java 来实现两个 List 的差集操作
1491 3