• list【2】模拟实现(含迭代器实现超详解哦)


    引言(实现概述)

    在前面,我们介绍了list的使用:
    戳我看list的介绍与使用详解哦

    在本篇文章中将重点介绍list的接口实现,通过模拟实现可以更深入的理解与使用list
    在这里插入图片描述
    我们模拟实现的 list 底层是一个带头双向循环链表

    在实现list时,我们首先需要一个结构体以表示链表中结点的结构list_node,大致包括数据与指向前后结点的指针

    template<class T>
    struct list_node  //结点类
    {
    	list_node<T>* _prev;
    	list_node<T>* _next;
    	T _date;
    
    	list_node(const T& date = T()) //匿名对象
    		: _prev(nullptr)
    		, _next(nullptr)
    		, _date(date)
    	{}
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在有了结点之后,还需要一个 list类,包括双向链表的头结点指针_pHead,链表中的元素个数_size

    template<class T>
    class list  //带头双向循环链表
    {
    	typedef list_node<T> Node;
    private:
    	Node* _pHead;
    	size_t _size;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    大致结构如下:
    在这里插入图片描述
    关于带头双向循环链表的实现可以参考之前的C语言版本实现,在本篇文章中结构将不是最重点的内容:戳我看C语言实现带头双向循环链表详解哦

    与vector相同,list是一个类模板,其声明与定义不能分离。我们将模拟实现的list放在我们创建的命名空间内,以防止与库发生命名冲突。
    在list的模拟实现中,我们只实现一些主要的接口,包括默认成员函数、迭代器、容量、元素访问与数据修改

    list迭代器实现

    与vector不同,list的迭代器是指针的封装,通过运算符重载来实现原生指针的功能
    我们可以通过封装结点的指针,即list_node*,来实现迭代器:

    默认成员函数

    对于默认成员函数,其实我们只需要实现构造函数即可,这个__list_iterator类的属性只有一个结构体指针,并不存在类中申请的资源,所以编译器生成的析构函数与赋值运算符重载就够用了,不需要再实现了。

    1. 默认构造
      对于默认构造函数,我们可以使用缺省参数,即参数类型为结构体指针,缺省值为nullptr
      直接在初始化列表中用参数初始化类属性即可:
    	__list_iterator(Node* pNode = nullptr)
    		:_pNode(pNode)
    	{}
    
    • 1
    • 2
    • 3
    1. 拷贝构造
      对于拷贝构造,参数必须是类类型的引用,可以加上const修饰(我们可以在类中将类类型__list_iterator重命名为self,以方便书写)
      在函数中直接赋值即可:
    	__list_iterator(const self& it)
    	{
    		_pNode = it._pNode;
    	}
    
    • 1
    • 2
    • 3
    • 4

    operator* 与 operator->

    迭代器使用时应该是与指针基本类似的,所以也需要重载*->

    1. 重载 *
      指针当然可以进行解引用的操作,指向容器中元素的指针。对这个指针进行解引用操作结果就应该是该指针指向的元素。对于list的迭代器而言,解引用该迭代器的结果就应该是结点中的_data元素的引用,类型为&T
      (我们可以为模板参数加上一个参数,即Ref,它表示T&
    	Ref operator*()
    	{
    		return _pNode->_date;
    	}
    
    • 1
    • 2
    • 3
    • 4
    1. 重载->
      T是自定义类型时,其指针还可以通过->直接访问到T类型对象_data中的元素,起指针作用的迭代器自然也需要实现这个功能
      但是对于这个运算符重载而言,它并不知道要返回T类型对象中的什么元素,所以这个operator->函数可以直接返回该迭代器对应的结点中的_data元素的指针,然后在调用的时候再通过->来访问其中元素:it->->a;(通过迭代器it访问T类型对象中的a元素)。
      但是,这样的形式访问又与指针的用法不一致,所以这里有一个特殊的规定,即规定需要使用it->a;的方式通过迭代器访问T类型对象中的元素,以获得与原生指针相同的使用方式
      (我们可以为模板参数加上一个参数,即Ptr,它表示T*
    	Ptr operator->()
    	{
    		return &(_pNode->_date);
    	}
    
    • 1
    • 2
    • 3
    • 4

    operator++ 与 operator–

    1. 重载 ++
      ++操作即实现迭代器的指向向后移动一个元素,list的迭代器底层是一个结构体指针,所以只需要将当前_pNode->_next 赋值给_pNode即可
      ++分为前置++与后置++,在区别这两个的实现时,前面类和对象时已经详细介绍过了,即给后置++增加一个参数int,但是不传参,只用于区分前置与后置。
      另外后置++需要创建临时对象,在*this++后必须要返回临时对象而非引用:
    	self& operator++()
    	{
    		_pNode = _pNode->_next;
    		return *this;
    	}
    	self operator++(int)
    	{
    		self temp(*this);
    		_pNode = _pNode->_next;
    		return temp;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 重载 --
      重载--与前面的++类似,即_pNode->_prev 赋值给_pNode即可
    	self& operator--()
    	{
    		_pNode = _pNode->_prev;
    		return *this;
    	}
    	self operator--(int)
    	{
    		self temp(*this);
    		_pNode = _pNode->_prev;
    		return temp;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    operator== 与 operator!=

    对于==!= 重载,即判断两个迭代器对象的属性是否相等即可

    	bool operator==(const self& it)
    	{
    		return _pNode == it._pNode;
    	}
    	bool operator!=(const self& it)
    	{
    		return _pNode != it._pNode;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    迭代器实现概览

    	template<class T, class Ref, class Ptr>
    	struct __list_iterator
    	{
    		typedef list_node<T> Node;
    		typedef __list_iterator<T, Ref, Ptr> self;
    
    		Node* _pNode;
    
    		__list_iterator(Node* pNode = nullptr)
    			:_pNode(pNode)
    		{}
    		__list_iterator(const self& it)
    		{
    			_pNode = it._pNode;
    		}
    
    
    		Ref operator*()
    		{
    			return _pNode->_date;
    		}
    		Ptr operator->()
    		{
    			return &(_pNode->_date);
    		}
    
    
    		self& operator++()
    		{
    			_pNode = _pNode->_next;
    			return *this;
    		}
    		self operator++(int)
    		{
    			self temp(*this);
    			_pNode = _pNode->_next;
    			return temp;
    		}
    		self& operator--()
    		{
    			_pNode = _pNode->_prev;
    			return *this;
    		}
    		self operator--(int)
    		{
    			self temp(*this);
    			_pNode = _pNode->_prev;
    			return temp;
    		}
    
    
    		bool operator==(const self& it)
    		{
    			return _pNode == it._pNode;
    		}
    		bool operator!=(const self& it)
    		{
    			return _pNode != it._pNode;
    		}
    	};
    
    • 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

    list主要接口实现

    在实现list之前,我们可以对一些较为麻烦的类型进行重命名以方便书写:

    typedef list_node<T> Node;
    typedef __list_iterator<T, T&, T*> iterator;
    typedef __list_iterator<T, const T&, const T*> const_iterator;
    
    • 1
    • 2
    • 3

    默认成员函数

    构造函数

    实现构造函数时,我们需要实现无参构造、n个指定元素构造、迭代器区间构造以及拷贝构造

    无参构造
    由于我们模拟实现的list底层为带头双向循环链表,所以在无参构造时虽然没有在list中放入元素,但是还使需要先放入一个空结点作为头节点
    创建头节点的操作在任何构造函数中都要先进行,所以我们将其封装为一个init_empty_list函数。这个初始化空list的函数需要new一个结点,然后使节点中的_prev_next都指向它自身,最后将_size赋值为0:

    	void init_empty_list()
    	{			
    		_pHead = new Node();
    		_pHead->_prev = _pHead;
    		_pHead->_next = _pHead;
    		_size = 0;
    	}
    	
    	list()
    	{
    		init_empty_list();
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    n个指定元素构造:
    使用n个指定元素构造有两个参数,第一个就是int,第二个是const T&,缺省值为T()
    函数中,首先调用init_empty_list构建一个头结点;
    再循环,使用push_bake尾插n个指定元素value即可(push_back后面会实现):

    	list(int n, const T& value = T())
    	{
    		init_empty_list();
    		while (n--)
    		{
    			push_back(value);
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    迭代器区间构造
    是用迭代器区间构造函数是一个函数模板,可以使用任何容器的迭代器区间来构造list
    函数中,首先调用init_empty_list构建一个头结点;
    然后再循环,使用push_bake尾插first迭代器的解引用出的元素,当firstlast相等时出循环:

    	template <class Iterator>
    	list(Iterator first, Iterator last)
    	{
    		init_empty_list();
    		while (first != last)
    		{
    			push_back(*first);
    			++first;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    拷贝构造:
    拷贝构造函数的参数是一个const list&
    实现时,首先调用init_empty_list构建一个头结点;
    然后范围for,使用push_bake将l中的元素逐一尾插到list中:

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

    析构函数

    析构函数需要实现释放list中的资源。
    首先复用clear,清空list中的元素。clear中会实现释放结点中的资源,后面部分会实现;
    delete _pHead;,释放头节点中的资源,它会调用结点结构体的析构函数

    	~list()
    	{
    		clear();
    		delete _pHead;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    赋值重载

    对于赋值运算符重载,我们直接使用新写法,即先使用参数l创建一个临时对象;
    然后使用swap将临时对象与*this交换(后面会实现swap函数);
    最后返回*this即可,创建的临时对象就会在函数栈帧销毁时自动释放

    	list<T>& operator=(const list<T>& l)  //list& operator=(const list l) 对于赋值重载,这样也可
    	{
    		list<T> temp(l);
    		swap(temp);
    		return *this;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    迭代器

    在前面已经实现了list的迭代器,它是结点指针的封装

    这里暂时只实现beginend,关于反向迭代器的实现在后面会详细介绍。
    begin返回首元素的地址,即头结点的下一个结点的地址
    end返回尾元素下一个位置的地址,即头节点的地址,他们分别重载有const版本:

    	iterator begin()
    	{
    		return iterator(_pHead->_next);
    	}
    	iterator end()
    	{
    		return iterator(_pHead);
    	}
    	
    	const_iterator begin() const
    	{
    		return const_iterator(_pHead->_next);
    	}
    	const_iterator end() const
    	{
    		return const_iterator(_pHead);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    容量

    在容器部分,由于list并没有容量的概念,所以我们只需要实现sizeempty即可;
    在这里插入图片描述
    我们在list的属性中,我们设置了_size,在插入元素时_size++,删除元素时_size--,所以这里只需要返回_size的值即可;
    _size == 0时,list为空,empty返回true,否者返回false

    	size_t size() const
    	{
    		return _size;
    	}
    	bool empty() const
    	{
    		if (_size == 0)
    			return true;
    		else
    			return false;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    元素访问

    由于list在任意位置访问元素的成本较高,就没有提供operator[]的接口,所以我们只需要实现frontback即可。分别返回首尾的元素,有普通对象与const对象两个重载版本:
    在这里插入图片描述
    在实现时,我们可以借助list的属性_pHead,即头结点的指针来访问首尾元素
    我们模拟实现list的底层是带头双向循环链表,所以list中的第一个元素就是_pHead->_next指向的结点中的元素;list中的_pHead->_prev指向的结点中的元素

    front只需要返回 _pHead->_next->_date 即可;
    back返回_pHead->_prev->_date即可,返回值类型为T&,const版本就返回const T&即可:

    	T& front()
    	{
    		return _pHead->_next->_date;
    	}
    	const T& front()const
    	{
    		return _pHead->_next->_date;
    	}
    	
    	T& back()
    	{
    		return _pHead->_prev->_date;
    	}
    	const T& back()const
    	{
    		return _pHead->_prev->_date;
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    数据修改

    在这里插入图片描述

    insert

    list 的结构使在任意位置插入数据的效率是较高的,只需要创建结点,再链接到pos位置前即可

    在实现insert时,首先new一个结点,类型为Node,并用val初始化(这个Node类型是前面重命名后的类型);
    这时我们需要记录pos位置的结点指针为curpos位置前面的结点指针为prev,以方便后续链接;
    然后将pos结点的前一个结点与新结点链接,即newnodeprev链接;
    再将pos结点与新结点链接,即newnodecur链接;

    最后,更新_size,并返回新结点的迭代器:

    	// 在pos位置前插入值为val的节点
    	iterator insert(iterator pos, const T& val)
    	{
    		Node* newnode = new Node(val);
    
    		Node* cur = pos._pNode;
    		Node* prev = cur->_prev;
    
    		newnode->_prev = prev;
    		prev->_next = newnode;
    
    		newnode->_next = cur;
    		cur->_prev = newnode;
    
    		++_size;
    
    		return iterator(newnode);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    erase

    erase实现时,只需要释放pos位置的结点,并链接剩余的结点即可:

    首先assert判断list是否为空;
    这时我们需要记录pos位置的结点指针为curpos位置前面的结点指针为prevpos的后一个结点指针为next,以方便后续链接;
    然后直接链接pos位置的前一个结点与后一个结点,即链接prevnext即可;

    最后,释放cur指向的结点,更新_size,并返回next

    	// 删除pos位置的节点,返回该节点的下一个位置
    	iterator erase(iterator pos)
    	{
    		assert(!empty());
    		Node* cur = pos._pNode;
    		Node* prev = cur->_prev;
    		Node* next = cur->_next;
    
    		prev->_next = next;
    		next->_prev = prev;
    
    		delete cur;
    		--_size;
    		return iterator(next);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    push_back 与 push_front

    对于头插与尾插的实现,复用insert即可:

    push_front,即在首结点的前面一个位置插入一个元素,即begin()迭代器位置插入
    push_back,即在尾结点的后一个位置插入一个元素,即end()位置插入

    	void push_back(const T& val)
    	{
    		insert(end(), val);
    	}
    	void push_front(const T& val)
    	{
    		insert(begin(), val);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    pop_back 与 pop_front

    对于头删尾删的实现,复用erase即可

    pop_front,即删除头节点,即 erase删除begin()位置的结点 即可;
    pop_back,删除尾结点,即 erase删除end()前面一个结点即可,但是由于list迭代器不支持-操作,所以这里传参为--end()

    	void pop_front()
    	{
    		erase(begin());
    	}
    	void pop_back()
    	{
    		erase(--end());
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    clear

    clear用于清理list中的所有元素,可以直接复用erase来实现清理

    我们可以通过遍历迭代器的方式逐一释放结点:
    it的初始值为begin(),循环逐一erase删除,当it等于end()的时候终止循环,就可以实现删除所有结点并保留头节点;
    另外,由于erase删除某节点后,会返回删除节点的下一个位置,所以只要把返回值载赋值给it就实现了迭代器的向后移动

    	void clear()
    	{
    		iterator it = begin();
    		while (it != end())
    		{
    			it = erase(it);//erase返回删除的结点的下一个位置的迭代器
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    swap

    实现list的交换函数时,只需要使用库swap交换list的属性即可,即交换_pHead_size

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

    源码概览

    (关于反向迭代器的实现在后面会详细介绍,现在可以暂时忽略)

    #include
    #include
    #include"my_reverse_iterator.h"
    
    namespace qqq
    {
    	template<class T>
    	struct list_node
    	{
    		list_node<T>* _prev;
    		list_node<T>* _next;
    		T _date;
    
    		list_node(const T& date = T()) //匿名对象
    			: _prev(nullptr)
    			, _next(nullptr)
    			, _date(date)
    		{}
    	};
    
    	template<class T, class Ref, class Ptr>
    	struct __list_iterator
    	{
    		typedef list_node<T> Node;
    		typedef __list_iterator<T, Ref, Ptr> self;
    
    		Node* _pNode;
    
    		__list_iterator(Node* pNode = nullptr)
    			:_pNode(pNode)
    		{}
    		__list_iterator(const self& it)
    		{
    			_pNode = it._pNode;
    		}
    
    
    		Ref operator*()
    		{
    			return _pNode->_date;
    		}
    		Ptr operator->()
    		{
    			return &(_pNode->_date);
    		}
    
    
    		self& operator++()
    		{
    			_pNode = _pNode->_next;
    			return *this;
    		}
    		self operator++(int)
    		{
    			self temp(*this);
    			_pNode = _pNode->_next;
    			return temp;
    		}
    		self& operator--()
    		{
    			_pNode = _pNode->_prev;
    			return *this;
    		}
    		self operator--(int)
    		{
    			self temp(*this);
    			_pNode = _pNode->_prev;
    			return temp;
    		}
    
    
    		bool operator==(const self& it)
    		{
    			return _pNode == it._pNode;
    		}
    		bool operator!=(const self& it)
    		{
    			return _pNode != it._pNode;
    		}
    	};
    
    	template<class T>
    	class list  //带头双向循环链表
    	{
    		typedef list_node<T> Node;
    	public:
    		typedef __list_iterator<T, T&, T*> iterator;
    		typedef __list_iterator<T, const T&, const T*> const_iterator;
    		typedef ReverseIterator<iterator, T&, T*> reverse_iterator;
    		typedef ReverseIterator<const_iterator, const T&, const T*> const_reverse_iterator;
    
    	public:
    		/ constructor and destructor 
    		void init_empty_list()
    		{			
    			_pHead = new Node();
    			_pHead->_prev = _pHead;
    			_pHead->_next = _pHead;
    
    			_size = 0;
    		}
    		list()
    		{
    			init_empty_list();
    		}
    		list(int n, const T& value = T())
    		{
    			init_empty_list();
    
    			while (n--)
    			{
    				push_back(value);
    			}
    		}
    		template <class Iterator>
    		list(Iterator first, Iterator last)
    		{
    			init_empty_list();
    			while (first != last)
    			{
    				push_back(*first);
    				++first;
    			}
    		}
    		list(const list<T>& l)
    		{
    			init_empty_list();
    			for (auto el : l)
    			{
    				push_back(el);
    			}
    		}
    		list<T>& operator=(const list<T>& l)  //list& operator=(const list l) 对于赋值重载,这样也可
    		{
    			list<T> temp(l);
    			swap(temp);
    			return *this;
    		}
    		~list()
    		{
    			clear();
    			delete _pHead;
    		}
    
    
    		// List Iterator///
    	
    		iterator begin()
    		{
    			return iterator(_pHead->_next);
    		}
    		iterator end()
    		{
    			return iterator(_pHead);
    		}
    		const_iterator begin() const
    		{
    			return const_iterator(_pHead->_next);
    		}
    		const_iterator end() const
    		{
    			return const_iterator(_pHead);
    		}
    		reverse_iterator rbegin()
    		{
    			return reverse_iterator(end());
    		}
    		reverse_iterator rend()
    		{
    			return reverse_iterator(begin());
    		}
    
    
    		/List Capacity//
    		size_t size() const
    		{
    			return _size;
    		}
    		bool empty() const
    		{
    			if (_size == 0)
    				return true;
    			else
    				return false;
    		}
    
    		///List Access///
    		T& front()
    		{
    			return _pHead->_next->_date;
    		}
    		const T& front()const
    		{
    			return _pHead->_next->_date;
    		}
    		T& back()
    		{
    			return _pHead->_prev->_date;
    		}
    		const T& back()const
    		{
    			return _pHead->_prev->_date;
    		}
    
    		List Modify//
    		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());
    		}
    		// 在pos位置前插入值为val的节点
    		iterator insert(iterator pos, const T& val)
    		{
    			Node* newnode = new Node(val);
    
    			Node* cur = pos._pNode;
    			Node* prev = cur->_prev;
    
    			newnode->_prev = prev;
    			prev->_next = newnode;
    
    			newnode->_next = cur;
    			cur->_prev = newnode;
    
    			++_size;
    
    			return iterator(newnode);
    		}
    		// 删除pos位置的节点,返回该节点的下一个位置
    		iterator erase(iterator pos)
    		{
    			assert(!empty());
    			Node* cur = pos._pNode;
    			Node* prev = cur->_prev;
    			Node* next = cur->_next;
    
    			prev->_next = next;
    			next->_prev = prev;
    
    			delete cur;
    			--_size;
    			return iterator(next);
    		}
    		void clear()
    		{
    			iterator it = begin();
    			while (it != end())
    			{
    				it = erase(it);//erase返回删除的结点的下一个位置的迭代器
    			}
    		}
    		void swap(list<T>& l)
    		{
    			std::swap(_pHead, l._pHead);
    			std::swap(_size, l._size);
    		}
    
    	private:
    		Node* _pHead;
    		size_t _size;
    	};
    };
    
    • 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

    总结

    到此,关于list的模拟实现就到此结束了
    模拟实现容器并不是为了造一个更好的轮子,而是为了更好的理解与使用容器

    如果大家认为我对某一部分没有介绍清楚或者某一部分出了问题,欢迎大家在评论区提出

    如果本文对你有帮助,希望一键三连哦

    希望与大家共同进步哦

  • 相关阅读:
    深入ftrace function原理
    (1.1)bark
    webpack自定义loader
    oracle高级—事务和锁
    自动驾驶仿真模型搭建
    HTML期末大学生网页-游戏官网 web期末作业设计网页
    vs运行localhos转换为本地ip地址访问
    react路由基础理解
    【Arduino+ESP32专题】模拟I/O的使用——PWM
    C语言之qsort()函数的模拟实现
  • 原文地址:https://blog.csdn.net/weixin_73450183/article/details/132419045