【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++常用基础知识—STL库(2)
C++常用基础知识—STL库(2)
68 5
|
30天前
|
存储 算法 C++
【C++打怪之路Lv10】-- list
【C++打怪之路Lv10】-- list
20 1
|
1月前
|
存储 自然语言处理 程序员
C++常用基础知识—STL库(1)
C++常用基础知识—STL库(1)
52 1
|
1月前
|
算法 安全 Linux
【C++STL简介】——我与C++的不解之缘(八)
【C++STL简介】——我与C++的不解之缘(八)
|
1月前
|
算法 数据处理 C++
c++ STL划分算法;partition()、partition_copy()、stable_partition()、partition_point()详解
这些算法是C++ STL中处理和组织数据的强大工具,能够高效地实现复杂的数据处理逻辑。理解它们的差异和应用场景,将有助于编写更加高效和清晰的C++代码。
22 0
|
1月前
|
存储 缓存 C++
C++番外篇——list与vector的比较
C++番外篇——list与vector的比较
21 0
|
1月前
|
C++
C++番外篇——list的实现
C++番外篇——list的实现
19 0
|
1月前
|
存储 C++ 容器
C++入门9——list的使用
C++入门9——list的使用
18 0
|
6天前
|
存储 编译器 C++
【c++】类和对象(中)(构造函数、析构函数、拷贝构造、赋值重载)
本文深入探讨了C++类的默认成员函数,包括构造函数、析构函数、拷贝构造函数和赋值重载。构造函数用于对象的初始化,析构函数用于对象销毁时的资源清理,拷贝构造函数用于对象的拷贝,赋值重载用于已存在对象的赋值。文章详细介绍了每个函数的特点、使用方法及注意事项,并提供了代码示例。这些默认成员函数确保了资源的正确管理和对象状态的维护。
29 4
|
7天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
25 4