【C++】C++ STL 探索:List使用与背后底层逻辑(二)

简介: 【C++】C++ STL 探索:List使用与背后底层逻辑

【C++】C++ STL 探索:List使用与背后底层逻辑(一)https://developer.aliyun.com/article/1617347


2.8 const_Iterator迭代器

2.8.1 实现const_Iterator迭代器

关于迭代器相关接口已经实现完毕,如果我们需要实现个指向内容不可以被修改的迭代器呢?

思考问题:

  1. 我们为什么要用const_Iterator而不是const Iterator呢?
  2. const修饰迭代器,是迭代器本身不能被修改,还是迭代器指向内容不能被修改呢?
const Iterator begin() const
{
    return _head->_next;
}
const Iterator end() const
{
    return _head;
}

我们实现const版本迭代器目的就是为了指向内容不被修改。

分析过程

  • Iterator是一个类,如果在前面加const,它表示的意思是这个类对象本身不能被修改,而不是指向内容不能被修改。
  • 实现链表的迭代器不是内置类型,而是自定义类型,在自定义类型前面使用const修饰代表本身不能被修改,会导致++或–运算符之类的运算符重载会失效。
  • 指出问题:迭代器指向内容是否被修改,需要对实现迭代器的类里面解引用运算符重载进行const修饰
template<class T>
    struct ListConIterator
    {
        typedef ListNode<T> Node;
        typedef  ListConIterator<T> Self;
        //只针对解引用 修改了迭代器的行为 但是++ --是将迭代器位置改变,迭代器指向的内容不能被修改
        Node* _node;
        //迭代器还是依赖指针,通过类来改变运算符的行为
        //这里是浅拷贝 拷贝构造函数
        ListIterator(Node* node)
            :_node(node)
            {}
        Self& operator++()
        {
            _node = _node->_next;
            return *this;
        }
        //后缀++
        Self operator++(int)
        {
            //构造一个tmp临时变量
            Self tmp(*this);
            _node = _node->_next;
            return tmp;
        }
        //这里需要判断空
        Self& operator--()
        {
            _node = _node->_prev;
            return *this;
        }
        //
        //后缀--
        Self operator--(int) 
        {
            //构造一个tmp临时变量
            Self tmp(*this);
            _node = _node->_prev;
            return tmp;
        }
        const  T& operator* () 
        {
            return _node->_data;
        }
        //通过指针指向的节点去判断是否相等
        bool operator==(const Self& it)
        {
            return _node == it._node;
        }
        bool operator!=(const Self& it)
        {
            return _node != it._node;
        }
        const T* operator->() 
        {
            return &_head->_data;
        }
    };

具体说明:将解引用操作符重载的返回值用const修饰,保证返回的数据不能被修改。这里没有对++和–的返回值进行修饰,因为++和–并不直接影响迭代器的内容,是针对迭代器(it)本身

2.8.2 模板整合类型问题

不足之处:对于Ierator类与const_Ierator类的解引用运算符重载大体功能相同,主要区别在于不同情况下需要使用返回值类型不同,实现两个类显得有点冗余。如果问题是在于类型上差异导致的,可以将两个封装到同一个类中,使用模板实现。

typedef ListIterator<T, T&, T*> Iterator;
typedef ListIterator<T, const T&, const T*> const_Iterator;
template<class T,class Ref, class Ptr>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef  ListIterator<T,Ref,Ptr> Self;
        Node* _node;
        //迭代器还是依赖指针,通过类来改变运算符的行为
        //这里是浅拷贝 拷贝构造函数
        ListIterator(Node* node)
            :_node(node)
            {}
        //Self& operator++()
        Self& operator++()
        {
            _node = _node->_next;
            return *this;
        }
        //后缀++
        Self operator++(int)
        {
            //构造一个tmp临时变量
            Self tmp(*this);
            _node = _node->_next;
            return tmp;
        }
        Self& operator--()
        {
            _node = _node->_prev;
            return *this;
        }
        //后缀--
        Self operator--(int)
        {
            //构造一个tmp临时变量
            Self tmp(*this);
            _node = _node->_prev;
            return tmp;
        }
        // T& operator* ()
        Ref operator* ()
        {
            return _node->_data;
        }
        //通过指针指向的节点去判断是否相等
        bool operator==(const Self& it)
        {
            return _node == it._node;
        }
        bool operator!=(const Self& it)
        {
            return _node != it._node;
        }
        //T* operator->()
        Ptr operator->()
        {
            return &_head->_data;
        }
    };

2.9 打印链表元素

void PrintList(const list<int>& clt)
{
    list<int> ::Iterator it = clt.begin();
    while (it != clt.end())
    {
        *it += 10;
        cout << *it << " ";
        ++it;
    }
    cout << endl;
}

三、常规接口实现

3.1 构造函数

void empty_init()
    {
        _head = new Node;
        _head->_next = _head;
        _head->_prev = _head;
        _size = 0;
    }

构造和拷贝构造都需要一份节点,那么可以复用一份

这里不需要对_data进行初始化,在new Node时已经完成

list()
    {
    empty_init();
    }

3.2 拷贝构造

list(const list<T>& lt)
    {
        empty_init();
        for (auto e : lt)
        {
            push_back(e);
        }
    }

3.3 operator= 赋值运算符

list<T>& operator=(list<T> it)
    {
        swap(it);      
        return *this;
    }

3.4 clear 清除

void clear()
{
    Iterator it = begin();
    while (it != end())
    {
        it = erase(it);
    }
}

说明:

  • 这里是简单的资源清除,没有删除哨兵位
  • 同时在使用erase时,需要将下一个位置迭代器返回

3.5 ~list 析构函数

~list()
{
    clear();        
    delete _head;
    _head = nullptr;
}

3.6 size 查看当前链表元素个数

//获得基本信息
    size_t size()
    {
        return _size;
    }

3.7 empty 判空

bool empty()
{
    return _size == 0;
}


【C++】C++ STL 探索:List使用与背后底层逻辑(三)https://developer.aliyun.com/article/1617350

相关文章
|
1天前
|
存储 搜索推荐 C++
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
21 2
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器2
|
1天前
|
存储 算法 C++
C++ STL 初探:打开标准模板库的大门
C++ STL 初探:打开标准模板库的大门
36 9
|
1天前
|
存储 C++ 容器
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器1
【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
23 5
|
1天前
|
存储 编译器 C++
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
【C++篇】揭开 C++ STL list 容器的神秘面纱:从底层设计到高效应用的全景解析(附源码)
18 2
|
4月前
|
安全 Java
java线程之List集合并发安全问题及解决方案
java线程之List集合并发安全问题及解决方案
591 1
|
3月前
|
Java API Apache
怎么在在 Java 中对List进行分区
本文介绍了如何将列表拆分为给定大小的子列表。尽管标准Java集合API未直接支持此功能,但Guava和Apache Commons Collections提供了相关API。
|
3月前
|
运维 关系型数据库 Java
PolarDB产品使用问题之使用List或Range分区表时,Java代码是否需要进行改动
PolarDB产品使用合集涵盖了从创建与管理、数据管理、性能优化与诊断、安全与合规到生态与集成、运维与支持等全方位的功能和服务,旨在帮助企业轻松构建高可用、高性能且易于管理的数据库环境,满足不同业务场景的需求。用户可以通过阿里云控制台、API、SDK等方式便捷地使用这些功能,实现数据库的高效运维与持续优化。
|
3月前
|
存储 安全 Java
详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法
详解Java中集合的List接口实现的ArrayList方法 | Set接口实现的HashSet方法
|
4月前
|
Java API
使用 Java 来实现两个 List 的差集操作
使用 Java 来实现两个 List 的差集操作
70 3
|
3月前
|
存储 Java 索引
Java List接口实现原理与性能评估
Java List接口实现原理与性能评估