【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

目录
打赏
0
2
2
0
21
分享
相关文章
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
C++ 标准模板库(STL)提供了一组功能强大的容器类,用于存储和操作数据集合。不同的容器具有独特的特性和应用场景,因此选择合适的容器对于程序的性能和代码的可读性至关重要。对于刚接触 C++ 的开发者来说,了解这些容器的基础知识以及它们的特点是迈向高效编程的重要一步。本文将详细介绍 C++ 常用的容器,包括序列容器(`std::vector`、`std::array`、`std::list`、`std::deque`)、关联容器(`std::set`、`std::map`)和无序容器(`std::unordered_set`、`std::unordered_map`),全面解析它们的特点、用法
C++ 容器全面剖析:掌握 STL 的奥秘,从入门到高效编程
【c++丨STL】priority_queue(优先级队列)的使用与模拟实现
本文介绍了STL中的容器适配器`priority_queue`(优先级队列)。`priority_queue`根据严格的弱排序标准设计,确保其第一个元素始终是最大元素。它底层使用堆结构实现,支持大堆和小堆,默认为大堆。常用操作包括构造函数、`empty`、`size`、`top`、`push`、`pop`和`swap`等。我们还模拟实现了`priority_queue`,通过仿函数控制堆的类型,并调用封装容器的接口实现功能。最后,感谢大家的支持与关注。
30 1
|
2月前
|
【c++丨STL】stack和queue的使用及模拟实现
本文介绍了STL中的两个重要容器适配器:栈(stack)和队列(queue)。容器适配器是在已有容器基础上添加新特性或功能的结构,如栈基于顺序表或链表限制操作实现。文章详细讲解了stack和queue的主要成员函数(empty、size、top/front/back、push/pop、swap),并提供了使用示例和模拟实现代码。通过这些内容,读者可以更好地理解这两种数据结构的工作原理及其实现方法。最后,作者鼓励读者点赞支持。 总结:本文深入浅出地讲解了STL中stack和queue的使用方法及其模拟实现,帮助读者掌握这两种容器适配器的特性和应用场景。
61 21
深入浅出 C++ STL:解锁高效编程的秘密武器
C++ 标准模板库(STL)是现代 C++ 的核心部分之一,为开发者提供了丰富的预定义数据结构和算法,极大地提升了编程效率和代码的可读性。理解和掌握 STL 对于 C++ 开发者来说至关重要。以下是对 STL 的详细介绍,涵盖其基础知识、发展历史、核心组件、重要性和学习方法。
【c++丨STL】list模拟实现(附源码)
本文介绍了如何模拟实现C++中的`list`容器。`list`底层采用双向带头循环链表结构,相较于`vector`和`string`更为复杂。文章首先回顾了`list`的基本结构和常用接口,然后详细讲解了节点、迭代器及容器的实现过程。 最终,通过这些步骤,我们成功模拟实现了`list`容器的功能。文章最后提供了完整的代码实现,并简要总结了实现过程中的关键点。 如果你对双向链表或`list`的底层实现感兴趣,建议先掌握相关基础知识后再阅读本文,以便更好地理解内容。
55 1
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
【C++篇】深度解析类与对象(中)
在上一篇博客中,我们学习了C++类与对象的基础内容。这一次,我们将深入探讨C++类的关键特性,包括构造函数、析构函数、拷贝构造函数、赋值运算符重载、以及取地址运算符的重载。这些内容是理解面向对象编程的关键,也帮助我们更好地掌握C++内存管理的细节和编码的高级技巧。
【C++篇】深度解析类与对象(上)
在C++中,类和对象是面向对象编程的基础组成部分。通过类,程序员可以对现实世界的实体进行模拟和抽象。类的基本概念包括成员变量、成员函数、访问控制等。本篇博客将介绍C++类与对象的基础知识,为后续学习打下良好的基础。
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等