list列表 - 指南

 list 容器的优点

  •         高效的插入和删除:由于std::list是基于带头双向循环链表实现的,插入和删除操作复杂度O(1)
  •         稳定的迭代器:相对于vector这种,list迭代器失效的情况比较少
  •         动态内存管理:std::list可以动态调整大小,根据需要分配和释放内存。能够有效地处理大量元素

但因为是链表结构,空间不连续,占用了更多的内存。在list 的迭代器当中也就没有实现 operator+ 这个函数。

定义私有成员:

	private:
		Node* _head;
		size_t _size;

在官方的List源代码当中,List容器的结点定义在一个结构体当中。

链表节点
	template                 //模板声明
	struct list_node
	{
		list_node* _next;          //成员变量
		list_node* _prev;
		T _val;
		list_node(const T& val = T()) //构造函数
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}
	};

使用 struct而不是 class,意味着所有成员默认是 public的。

定义正向迭代器

通过模板参数来同时实现 普通迭代器 和 const 迭代器,避免代码冗余。

	// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)
	template
	struct __list_iterator                          //定义一个类来实现迭代起的行为
	{
		typedef list_node Node;
		typedef __list_iterator self;  //给自己类型起个别名,简化代码
		Node* _node;                                //节点指针
		__list_iterator(Node* node)                 //构造函数
			:_node(node)
		{}
		// 运算符重载
		Ref operator*()                  // 解引用
		{
			return _node->_val;
		}
		Ptr operator->()                 // 成员访问(编译器会优化掉一个->)
		{
			return &_node->_val;
		}
		//前置++
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}
		bool operator==(const self& it) const
		{
			return _node == it._node;
		}
};

举例使用

定义反向迭代器
  1. 核心思想​​:rbegin()对应 end()(即最后一个元素的下一个),rend()对应 begin()

  2. ​​操作​​:对反向迭代执行 ++操作,实际是调用其内部封装的正向迭代器的 --操作。

	template
	struct __list_reverse_iterator
	{
		typedef list_node Node;
		typedef __list_reverse_iterator self;
		Node* _node;
		__list_reverse_iterator(Node* node)
			:_node(node)
		{}
		Ref operator*()
		{
			return _node->_val;
		}
		Ptr operator->()
		{
			return &_node->_val;
		}
		// 注意:反向迭代器的++是正向的--
		self& operator++()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		// 反向迭代器的--是正向的++
		self& operator--()
		{
			_node = _node->_next;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}
		bool operator==(const self& it) const
		{
			return _node == it._node;
		}
	};
定义一个list类

对之前

  • list_node结构体 重命名为 Node
  • 正向迭代器重命名为 iterator 和 const_iterator
  • 反向迭代器重命名为 reverse_iterator 和 const_reverse_iterator
	template
	class list
	{
		typedef list_node Node; //重命名
	public:
		//正向迭代器
		typedef __list_iterator iterator;
		typedef __list_iterator const_iterator;
		//反向迭代器
		typedef __list_reverse_iterator reverse_iterator;
		typedef __list_reverse_iterator const_reverse_iterator;
        ......
    };

构造函数

		void empty_init()       //创建一个空指针
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}
		list()                //构造函数
		{
			empty_init();
		}

析构函数

		~list()
		{
			clear();                //析构函数
			delete _head;
			_head = nullptr;
		}
		void clear()                //清理
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			_size = 0;
		}

拷贝构造函数

		// lt2(lt1)
		list(const list& lt)
			//list(const list& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}

push_back

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

push_front

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

pop_back

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

pop_front

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

insert

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			prev->_next = newnode;
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			++_size;
			return newnode;
		}

要返回新插入的元素的位置,防止外部迭代器失效。 

erase

		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			--_size;
			return next;
		}

size()

		size_t size()	{ return _size;	}

empty

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

front()

		T& front()
		{
			assert(_size > 0);
			return _head->_next->_val;
		}

back()

		T& back()
		{
			assert(_size > 0);
			return _head->_prev->_val;
		}

赋值运算符重载函数和swap()函数

		void swap(list& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
		list& operator=(list lt)
			//list& operator=(list lt)
		{
			swap(lt);
			return *this;
		}

其实上述的 赋值重载运算符函数当中的模板类的类型名可以不用 List<T>,我们知道List<T>是类型名,List是类名,对于模版类,类型名不是 List,而是List<T>,但是如果是在类模板当中写,可以写类名也可以写类型名(下述写法也可以):

list& operator=(list lt)

总代码
#pragma once
#include
#include 
using namespace std;
namespace bit
{
	template
	struct list_node      //底层是结构体
	{
		list_node* _next;
		list_node* _prev;
		T _val;
		list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}
	};
	// typedef __list_iterator iterator;
	// typedef __list_iterator const_iterator;
	// T: 数据类型, Ref: 引用类型(T&/const T&), Ptr: 指针类型(T*/const T*)
	template
	struct __list_iterator
	{
		typedef list_node Node;
		typedef __list_iterator self;  // 简化类型名
		Node* _node;// 核心:持有一个指向节点的指针
		__list_iterator(Node* node)
			:_node(node)
		{}
		// 运算符重载
		Ref operator*()// 解引用
		{
			return _node->_val;
		}
		Ptr operator->() // 成员访问(编译器会优化掉一个->)
		{
			return &_node->_val;
		}
		//前置++
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}
		bool operator==(const self& it) const
		{
			return _node == it._node;
		}
	};
	// 反向迭代器
	template
	struct __list_reverse_iterator
	{
		typedef list_node Node;
		typedef __list_reverse_iterator self;
		Node* _node;
		__list_reverse_iterator(Node* node)
			:_node(node)
		{}
		Ref operator*()
		{
			return _node->_val;
		}
		Ptr operator->()
		{
			return &_node->_val;
		}
		// 注意:反向迭代器的++是正向的--
		self& operator++()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		// 反向迭代器的--是正向的++
		self& operator--()
		{
			_node = _node->_next;
			return *this;
		}
		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		bool operator!=(const self& it) const
		{
			return _node != it._node;
		}
		bool operator==(const self& it) const
		{
			return _node == it._node;
		}
	};
	/*template
	struct __list_const_iterator
	{
		typedef list_node Node;
		Node* _node;
		__list_const_iterator(Node* node)
			:_node(node)
		{}
		const T& operator*()
		{
			return _node->_val;
		}
		__list_const_iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		__list_const_iterator operator++(int)
		{
			__list_const_iterator tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		bool operator!=(const __list_const_iterator& it)
		{
			return _node != it._node;
		}
		bool operator==(const __list_const_iterator& it)
		{
			return _node == it._node;
		}
	};*/
	template
	class list
	{
		typedef list_node Node; //重命名
	public:
		//正向迭代器
		typedef __list_iterator iterator;
		typedef __list_iterator const_iterator;
		//反向迭代器
		typedef __list_reverse_iterator reverse_iterator;
		typedef __list_reverse_iterator const_reverse_iterator;
		// 这么设计太冗余了,看看库里面如何设计的
		//typedef __list_const_iterator const_iterator;
		// 这样设计const迭代器是不行的,因为const迭代器期望指向内容不能修改
		// 这样设计是迭代器本身不能修改
		// typedef const __list_iterator const_iterator;
		// const T* ptr1;
		// T* const ptr2;
		// 如何设计const迭代器?
		iterator begin()
		{
			//return _head->_next;
			return iterator(_head->_next);
		}
		iterator end()
		{
			return _head;
			//return iterator(_head);
		}
		const_iterator begin() const
		{
			//return _head->_next;
			return const_iterator(_head->_next);
		}
		const_iterator end() const
		{
			return _head;
			//return const_iterator(_head);
		}
		// 反向迭代器的起始是最后一个元素
		reverse_iterator rbegin()
		{
			return reverse_iterator(_head->_prev);
		}
		// 反向迭代器的结束是头节点(第一个元素的前一个位置)
		reverse_iterator rend()
		{
			return reverse_iterator(_head);
		}
		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(_head->_prev);
		}
		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(_head);
		}
		void empty_init()         //ok
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
			_size = 0;
		}
		list()                  //构造函数
		{
			empty_init();
		}
		// lt2(lt1)
		list(const list& lt)   //拷贝构造函数
			//list(const list& lt)
		{
			empty_init();
			for (auto& e : lt)
			{
				push_back(e);
			}
		}
		void swap(list& lt)      //swap
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}
		list& operator=(list lt)    //赋值运算符重载
			//list& operator=(list lt)
		{
			swap(lt);
			return *this;
		}
		~list()          //析构函数
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()      // ok
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
			_size = 0;
		}
		void push_back(const T& x)  //
		{
			insert(end(), x);
		}
		void push_front(const T& x) //
		{
			insert(begin(), x);
		}
		void pop_back()				//
		{
			erase(--end());
		}
		void pop_front()			//
		{
			erase(begin());
		}
		// pos位置之前插入
		iterator insert(iterator pos, const T& x) //insert
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			prev->_next = newnode;
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			++_size;
			return newnode;
		}
		iterator erase(iterator pos)  //erase
		{
			assert(pos != end());
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			--_size;
			return next;
		}
		size_t size()          //ok
		{
			/*size_t sz = 0;
			iterator it = begin();
			while (it != end())
			{
				++sz;
				++it;
			}
			return sz;*/
			return _size;
		}
		bool empty() const { return _size == 0; }
		T& front()
		{
			assert(_size > 0);
			return _head->_next->_val;
		}
		T& back()
		{
			assert(_size > 0);
			return _head->_prev->_val;
		}
	private:
		Node* _head;
		size_t _size;
	};

list与vector的区别
vector        list
底 层 结 构动态顺序表,是一段连续空间带头结点的双向循环链表
随 机 访 问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素
效率O(N)
插 入 和 删 除任意位置插入和删除效率低,需要搬移元素,时间复杂
度为O(N),插入时有可能需要增容,增容:开辟新空
间,拷贝元素,释放旧空间,导致效率更低
任意位置插入和删除效率高,不
需要搬移元素,时间复杂度为
O(1)
空 间 利 用 率底层为连续空间,不容易造成内存碎片,空间利用率
高,缓存利用率高
底层节点动态开辟,小节点容易
造成内存碎片,空间利用率低,
缓存利用率低
迭 代 器原生态指针对原生态指针(节点指针)进行封装
迭 代 器 失 效失效情况相对较多失效情况相对较少
使 用 场 景需要随机访问,不关心插入删除大量插入和删除操作,不关心随
机访问

posted @ 2025-10-28 17:28  yangykaifa  阅读(5)  评论(0)    收藏  举报