首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

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

  • 25-03-07 12:00
  • 2849
  • 9610
blog.csdn.net

在这里插入图片描述

C++语法相关知识点可以通过点击以下链接进行学习一起加油!
命名空间缺省参数与函数重载C++相关特性类和对象-上篇类和对象-中篇
类和对象-下篇日期类C/C++内存管理模板初阶String使用
String模拟实现Vector使用及其模拟实现

本文将通过模拟实现List,从多个角度深入剖析其底层机制,详细讲解其内部实现原理和实际应用场景,帮助读者全面理解和掌握List的工作方式。

请添加图片描述
Alt
?个人主页:是店小二呀
?C语言专栏:C语言
?C++专栏: C++
?初阶数据结构专栏: 初阶数据结构
?高阶数据结构专栏: 高阶数据结构
?Linux专栏: Linux

?喜欢的诗句:无人扶我青云志 我自踏雪至山巅 请添加图片描述

文章目录

  • 前文:List介绍
  • 一、搭建创建List武器库
    • 1.1 创建节点ListNode
    • 1.2 迭代器ListIterator
      • 1.2.1 自定义类型迭代器
      • 1.2.2 交换语句
      • 1.2.3 迭代器运算符重载
        • 1.2.3.1 前置++与后置++
        • 1.2.3.2 前置--与后缀--
        • 1.2.3.4 *运算符重载
        • 1.2.3.5 ==与!=运算符重载
  • 二、正式模拟实现List
    • 2.1 武器库使用
    • 2.2 获得List开头和结尾迭代器
    • 2.3 关于迭代器失效
    • 2.4 insert
    • 2.5 erase
    • 2.6 复用insert与erase完成插入与删除
    • 2.7 operator->重载
    • 2.8 const_Iterator迭代器
      • 2.8.1 实现const_Iterator迭代器
      • 2.8.2 模板整合类型问题
    • 2.9 打印链表元素
  • 三、常规接口实现
    • 3.1 构造函数
    • 3.2 拷贝构造
    • 3.3 operator= 赋值运算符
    • 3.4 clear 清除
    • 3.5 ~list 析构函数
    • 3.6 size 查看当前链表元素个数
    • 3.7 empty 判空
  • List.h

前文:List介绍

list文档介绍

  • list是可以在常数范围内在任意位置插入和删除的序列式容器,并且该容器可以前后双向迭代
  • list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前面一个元素和后一个元素
  • list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效
  • 与其他的序列式容器相比,list通常在任意位置进行插入,移除元素的执行效率更好
  • 与其他序列式容器相比,list和forward_list最大的缺陷式不支持任意位置的随机访问。

在这里插入图片描述

在模拟实现list过程中,为了避免跟库中list发生冲突,需要创建个命名空间,在命名空间中实现list。

namespace ls
{
	class list
	{
		
	};
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

list的底层是双向链表结构,而链表是通过每个独立的节点利用指针连接在一起的数据结构。节点是组成链表基本单位。模拟实现带头双向循环链表,为了适应不同的数据类型,选择采用类模板。

一、搭建创建List武器库

1.1 创建节点ListNode

template<class T>
    struct ListNode
    {
        ListNode<T>* _next;
        ListNode<T>* _prev;
        T _data;

        //创建节点,是传数据创建的
        ListNode(const T& x = T())
            :_next(nullptr)
                ,_prev(nullptr)
                ,_data(x)
            {}
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

具体说明:为了适应不同类型的节点,将创建节点设计成类模板。访问限定符一般选用struct,由于默认访问权限是公开的,节点里面数据都是可以使用。当然使用class也是可以的,只需要将访问限定符设置为public。

节点对象实例化一般是传个数值,但是为了考虑类型和是否传参,可以设置一个全缺省构造函数

1.2 迭代器ListIterator

链表通过每个节点连接在一起,但这不能保证节点间地址是连续的。对此虽然原生指针是天然的迭代器,但是建立在物理空间连续的情况下

1.2.1 自定义类型迭代器

list<T> ::iterator it = lt.begin();
while (it != lt.end())
{
    *it += 10;
    cout << *it;
    it++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

由于链表节点间物理空间不连续,使用原生指针作为迭代器不能满足++或–操作。节点间又是通过指针连接在一起的,那么可以将指针封装到类中,通过运算符重载重新定义运算符的行为,达成我们的需求。不采用内置类型迭代器,选择自定义类型迭代器,其中自定义类型迭代器内部是通过指针实现的。

template<class T>
    struct ListIterator
    {
        typedef ListNode<T> Node;
        typedef  ListIterator<T> Self;
        Node* _node;

        ListIterator(Node* node)
            :_node(node)
            {}
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注意事项:

  • 将通过ListIterator类模板模拟实现迭代器,通过采用struct公开迭代器里面的数据
  • ListIterator(Node* node),这里迭代器的实现还是依赖指针,只是通过类封装改变运算符的行为,参数部分是传递指针类型(关键)
  • 其中迭代器是作为指向作用,申请资源不属于迭代器,而属于链表,不需要考虑析构的问题,迭代器就是玩数据
  • 数据公开意味着,内部数据可以被任意修改。没人会去跳过封装,使用内部的数据,没有意义。不同编译器中底层实现是不一样的(实现逻辑、名称),这本身就是一种隐式设置为私有的作用

1.2.2 交换语句

void swap(list<T>& it)
{
    std::swap(_head, it._head);
    std::swap(_size, it._size);
}
  • 1
  • 2
  • 3
  • 4
  • 5

1.2.3 迭代器运算符重载

1.2.3.1 前置++与后置++
//前缀++
Self& operator++()
{
    _node = _node->_next;
    return *this;
}

//后缀++
Self operator++(int)
{
    //构造一个tmp临时变量
    Self tmp(*this);
    _node = _node->_next;

    return tmp;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
1.2.3.2 前置–与后缀–
Self& operator--()
{
    _node = _node->_prev;
    return *this;
}

//后缀--
Self operator--(int)
{
    //构造一个tmp临时变量
    Self tmp(*this);
    _node = _node->_prev;

    return tmp;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

具体说明:有拷贝构造就需要考虑深浅拷贝的问题。这里需要使用到浅拷贝,指向同一块空间,并且不需要考虑重复析构的问题,也说明了浅拷贝并都是坏处。临时对象tmp同指向一块空间,调用完临时对象被销毁,指向空间资源保留。这也导致了返回类型是指针还是引用。

1.2.3.4 *运算符重载
T& operator* ()
{
    return _node->_data;
}
  • 1
  • 2
  • 3
  • 4
1.2.3.5 ==与!=运算符重载
//通过指针指向的节点去判断是否相等
bool operator==(const Self& it)
{
    return _node == it._node;
}

bool operator!=(const Self& it)
{
    return _node != it._node;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

具体说明:

  • 关于形参部分使用const修饰,其一为了提醒对象顺序问题,其二在while(it != lt.end())中lt.end()调用返回临时对象具有常性,在参数部分进行接收将权限降低。
  • 迭代器间的比较并不是比较指向数据,而是比较迭代器指向的位置

二、正式模拟实现List

2.1 武器库使用

前面知识是为了我们实现List提供武器库

template<class T>
    class list
    {
        typedef ListNode<T> Node;
        public:
        typedef  ListIterator<T> Iterator;

        private:
        //创建哨兵位
        Node* _head;
        size_t _size;
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在这里插入图片描述

武器使用:

  • 关于typedef ListNode Node与typedef ListIterator Iterator这两个类可以当作武器库,主要是为List类提供服务。那么对于ListIterator类来说ListNode Node也是当作一个武器进行使用。
  • 对于带头双向循环链表,成员对象需要哨兵位和记录个数对象。

2.2 获得List开头和结尾迭代器

Iterator begin()
{
    //return Iterator(_head->_next);
    return _head->_next;
}

Iterator end()
{
    //return Iterator(_head);
    return _head;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

具体说明:

  • 由于正在使用自定义类型的迭代器,返回类型为Iterator自定义类型。返回类型可以写成Node*类型,单参数的拷贝构造函数支持隐式类型转换。

需要注意:

  • 返回值没有传引用返回,由于该接口功能是返回开头和节点迭代器,如果传引用返回,会影响下一次调用该节点。我们不需要拿到这个位置的迭代器,只需要拿到这个位置的信息。

小插曲:隐式类型转换

list<int> :: Iterator it = (lt._head->_next);
  • 1

由于_ head属于list类中的私有对象,不能直接的访问私有成员,通过公共接口访问。不同编译器底层实现是不同的,这也体现了封装的重要性。

2.3 关于迭代器失效

关于vector学习,我们知道在扩容或缩容的时候,可能会出现迭代器失效的问题。在list这里insert不会出现迭代器失效,但是erase就会出现迭代器失效。

在这里插入图片描述

关于解决不同编译器下erase导致迭代器失效的问题。方法有两个:迭代器失效以后,不要直接使用,如何要使用按照规则重新更新使用,返回被删除数据的迭代器。

2.4 insert

void  insert(Iterator pos, const T& val)
{
    Node* newnode = new Node(val);

    Node* cur = pos._node;
    Node* prev = cur->_prev;

    newnode->_next = cur;
    newnode-> _prev = prev;
    cur->_prev = newnode;
    prev->_next = newnode;

    _size++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这里跟数据结构实现双向链表任意位置插入数据的逻辑是一样的,不同的地方就是这里使用迭代器。

2.5 erase

//这些需要使用迭代器作为返回类型,怕出现迭代器失效
Iterator& erase(Iterator pos)
{
    Node* cur = pos._node;
    Node* next = cur->_next;
    Node* prev = cur->_prev;

    delete cur;
    next->_prev = prev;
    prev->_next = next;

    _size--;

    //注意点
    return Iterator(next);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

具体说明:这里跟数据结构实现双向链表任意位置删除数据的逻辑是一样的。

需要注意:返回类型需要使用迭代器类型,怕出现迭代器失效,返回下一个位置的迭代器

2.6 复用insert与erase完成插入与删除

void push_back(const T& val)
{
    insert(end(), val);
}

void pop_back()
{
    erase(--end());
}

void push_front(const T& val)
{
    insert(begin(), val);
}

void pop_front()
{
    erase(begin());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

具体说明:

  • 在vector实现push_back和pop_back时,通过begin和end得到迭代器指向的位置。返回变量为具有常性的临时变量,不能通过++或–对其修改。
  • 在List中迭代器可以进行++或–操作,由于不是对临时对象本身进行修改,而是在运算符重载中改变了运算符的行为,修改是临时对象指向的内容。在vector中修改是对象本身当然是不信的。

2.7 operator->重载

给出场景:关于之前类模板实例化中模板参数为内置类型,这次将T改为自定义类型,注意A是自定义类型。

struct A
{
    int a1 = 0, a2 = 0;

    A(int a1,int a2)
        :a1(1)
            ,a2(2)
        {}
};
list<A> lt;
list<A> ::Iterator it = lt.begin();

lt.push_back(A(1,2));
lt.push_back({3,3});
while (it != lt.end())
{
    cout << *it;
    it++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

在进行cout << *it中得到该类对象,并没有得到内部数据,对此有两个解决措施:

  1. (*it).a1直接访问成员
  2. 流插入的运算符重载

我们希望得到的效果是第二种写法,第一种感觉会冗余

  • 第一种:(*it).a1

  • 第二种:it->_a1

重点梳理:

  • 先梳理思路list ::Iterator it代表了it属于Iterator类对象。这里将it看成迭代器,其中*被运算符重载为_node->_data,其中_data类型就是自定义类型A。
  • 那么*it行为就是得到自定义类型A对象,通过A对象.内部成员变量方式进行访问。
  • 对于第二种:也是想通过->运算符重载得到A类型对象,但是很奇怪,得到了A类型对象怎么去访问A类型对象中成员变量呢?对此编译器为了可读性,省略了一个->,实际上是这样子的it.operator->()->_a1,编译器省略之后it->_a1就可以了,提高了可读性。
//返回A类型对象指针,很合理啦
T* operator->()
{
    return &_node->_data;
}
  • 1
  • 2
  • 3
  • 4
  • 5

在这里插入图片描述

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

我们实现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;
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71

具体说明:将解引用操作符重载的返回值用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;
        }
    };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73

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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

三、常规接口实现

3.1 构造函数

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

        _size = 0;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

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

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

    list()
    {
    empty_init();
    }
  • 1
  • 2
  • 3
  • 4

3.2 拷贝构造

    list(const list<T>& lt)
    {
        empty_init();
        for (auto e : lt)
        {
            push_back(e);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3.3 operator= 赋值运算符

    list<T>& operator=(list<T> it)
    {
        swap(it);			 
        return *this;
    }
  • 1
  • 2
  • 3
  • 4
  • 5

3.4 clear 清除

void clear()
{
    Iterator it = begin();
    while (it != end())
    {
        it = erase(it);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

说明:

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

3.5 ~list 析构函数

~list()
{
    clear();				
    delete _head;
    _head = nullptr;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

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

    //获得基本信息
    size_t size()
    {
        return _size;
    }
  • 1
  • 2
  • 3
  • 4
  • 5

3.7 empty 判空

bool empty()
{
    return _size == 0;
}
  • 1
  • 2
  • 3
  • 4

List.h

#include 

using namespace std;

//链表 任意位置插入 不怕迭代器失效
//链表 任意位置删除 怕迭代器失效 对此需要传下一个位置的迭代器
namespace bit
{
	//节点
	template<class T>
	struct ListNode
	{
		ListNode<T>* _next;
		ListNode<T>* _prev;
		T _data;

		//创建节点,是传数据创建的
		ListNode(const T& x = T())
			:_next(nullptr)
			,_prev(nullptr)
			,_data(x)
		{}
	};

	//template
	//
	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;
		 }
	};

	//template
	//struct ListConIterator
	//{
	//	typedef ListNode Node;
	//	typedef  ListConIterator 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;
	//	}
	//	 
	//	 T& operator* () 
	//	{
	//		return _node->_data;
	//	}

	//	//通过指针指向的节点去判断是否相等
	//	bool operator==(const Self& it)
	//	{
	//		return _node == it._node;
	//	}

	//	bool operator!=(const Self& it)
	//	{
	//		return _node != it._node;
	//	}

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

	template<class T>
	class list
	{
		typedef ListNode<T> Node;
		public:
			//typedef  ListIterator Iterator;
			typedef ListIterator<T, T&, T*> Iterator;
			typedef ListIterator<T, const T&, const T*> const_Iterator;

			//不要传引用,不要修改这些位置
			Iterator begin()
			{
				//return Iterator(_head->_next);
				//单参数的拷贝构造函数支持隐式类型转换
				return _head->_next;
			}

			Iterator end()
			{
				//return Iterator(_head);
				return _head;

			}

			const Iterator begin() const
			{
				return _head->_next;
			}
			const Iterator end() const
			{
				return _head;
			}
			const_Iterator begin() const
			{
				//return Iterator(_head->_next);
				//单参数的拷贝构造函数支持隐式类型转换
				return _head->_next;
			}

			const_Iterator end() const
			{
				//return Iterator(_head);
				return _head;

			}

			//构造-->同时拷贝需要也需要一份节点 那么就可以复用同一份
			void empty_init()
			{
				_head = new Node;
				_head->_next = _head;
				_head->_prev = _head;
				//不需要 newndoe->date 在实例化时,data已经是0了
				_size = 0;
			}
			
			list()
			{
				empty_init();
			}
			
			//拷贝构造
			 list(const list<T>& lt)
			{
				empty_init();
				for (auto e : lt)
				{
					push_back(e);
				}
			}
			
			 list<T>& operator=(list<T>& it)
			 {
				 swap(it);			 
				 return *this;
			 }

			 void swap(list<T>& it)
			 {
				 std::swap(_head, it._head);
				 std::swap(_size, it._size);
			 }

			 //通过迭代器来实现
			void  insert(Iterator pos, const T& val)
			 {
				 Node* newnode = new Node(val);

				 Node* cur = pos._node;
				 Node* prev = cur->_prev;
				 
				 newnode->_next = cur;
				 newnode-> _prev = prev;
				 cur->_prev = newnode;
				 prev->_next = newnode;

				 _size++;
			 }

			//这些需要使用迭代器作为返回类型,怕出现迭代器失效
			Iterator& erase(Iterator pos)
			 {
				Node* cur = pos._node;
				Node* next = cur->_next;
				Node* prev = cur->_prev;

				delete cur;
				next->_prev = prev;
				prev->_next = next;
				
				_size--;

				//注意点
				return Iterator(next);
			 }
			
			void push_back(const T& val)
			{
				insert(end(), val);
			}

			void pop_back()
			{
				erase(--end());
			}

			void push_front(const T& val)
			{
				insert(begin(), val);
			}

			void pop_front()
			{
				erase(begin());
			}

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

			~list()
			{
				clear();				
				delete _head;
				_head = nullptr;
			}
			 //获得基本信息
			 size_t size()
			 {
				 return _size;
			 }

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


		private:
			//创建哨兵位
			Node* _head;
			size_t _size;
	};

	struct A
	{
		int a1 = 0, a2 = 0;
		
		A(int a1 , int a2)
			:a1(1)
			,a2(2)
		{}
	};

	void PrintList(const list<int>& clt)
	{
		list<int> ::Iterator it = clt.begin();
		while (it != clt.end())
		{
			*it += 10;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
	void list_test()
	{
		//list it;
		先插入
		//it.push_back(1);
		//it.push_back(2);

		//list :: Iterator lt = it.begin();

		
		//A* ptr = &a1
		// (*ptr).a1;
		// ptr->_a1
		//返回的是临时变量 是一份临时拷贝 具有常性

		list<A> lt;
		list<A> ::Iterator it = lt.begin();

		lt.push_back(A(1,2));
		lt.push_back({3,3});
		while (it != lt.end())
		{
			cout << *it;
			it++;
		}

	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371
  • 372
  • 373
  • 374
  • 375
  • 376
  • 377
  • 378
  • 379
  • 380
  • 381
  • 382
  • 383
  • 384
  • 385
  • 386
  • 387
  • 388


以上就是本篇文章的所有内容,在此感谢大家的观看!这里是店小二呀C++笔记,希望对你在学习C++语言旅途中有所帮助!
请添加图片描述

注:本文转载自blog.csdn.net的是店小二呀的文章"https://blog.csdn.net/2302_79177254/article/details/141396079"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2025 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top