【C++】list的模拟实现

list 的常见接口介绍

  1. 构造函数
函数名称 功能说明
list() 构造空的list
list(const list& x) 拷贝构造
list(InputIterator first, InputIterator last) 用迭代器区间构造list
list(int n, const T& val = T()) 用 n 个 val 元素构造list
  1. 遍历及访问操作
函数名称 功能说明
begin() 返回第一个元素的迭代器
end() 返回最后一个元素下一个位置的迭代器
rbegin() 返回 end() 位置的反向迭代器
rend() 返回 begin() 位置的反向迭代器
front() 获取 list 中第一个元素的引用
back() 获取 list 中最后一个元素的引用
  1. 容量操作
函数名称 功能说明
empty() 判断 list 是否为空
size() 返回 list 中有效节点的个数
  1. 修改操作
函数名称 功能说明
push_front(const T& val) 在 list 第一个元素之前插入 val 元素
push_back(const T& val) 在 list 最后一个元素之后插入 val 元素
pop_front() 删除 list 中的第一个节点
pop_back() 删除 list 中的最后一个节点
insert(iterator pos, const T& val) 在 pos 位置插入 val 元素
erase(iterator pos) 删除 pos 位置的节点
swap(list& lt) 两个 ilst 进行相互交换
clear() 清除 list 中的有效节点

list 的模拟实现

cpp 复制代码
#pragma once
#include <cassert>

namespace zs
{
	template<class T>
	class list_node
	{
	public:
		list_node(const T& val = T())
			: _data(val)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	public:
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;
	};
	
	// 反向迭代器
	template<class Iterator, class Ref, class Ptr>
	class __reverse_iterator
	{
	public:
		typedef __reverse_iterator<Iterator, Ref, Ptr> reverse_iterator;
	public:
		__reverse_iterator(Iterator it)
			:_cur(it)
		{}

		reverse_iterator& operator++()
		{
			--_cur;
			return *this;
		}

		reverse_iterator& operator--()
		{
			++_cur;
			return *this;
		}

		reverse_iterator operator++(int)
		{
			reverse_iterator tmp = *this;
			--_cur;
			return tmp;
		}

		reverse_iterator operator--(int)
		{
			reverse_iterator tmp = *this;
			++_cur;
			return tmp;
		}

		Ref operator*()
		{
			Iterator tmp = _cur;
			--tmp;
			return *tmp;
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		bool operator!=(const reverse_iterator& it)
		{
			return _cur != it._cur;
		}

		bool operator==(const reverse_iterator& it)
		{
			return _cur == it._cur;
		}
	public:
		Iterator _cur;
	};
	
	// 正向迭代器
	template<class T, class Ref, class Ptr>
	class __list_iterator
	{
	private:
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> iterator;
	public:
		__list_iterator(Node* node)
			: _node(node)
		{}

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

		bool operator==(const iterator& it) const
		{
			return _node == it._node;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return &(operator*());
		}

		iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		iterator operator++(int)
		{
			iterator ret(*this);
			_node = _node->_next;
			return ret;
		}

		iterator& operator--()
		{
			_node = _node->_prev;
			return *this;
		}

		iterator operator--(int)
		{
			iterator ret(*this);
			_node = _node->_prev;
			return ret;
		}
	public:
		Node* _node;
	};

	template<class T>
	class list
	{
	private:
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T, T&, T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		typedef __reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

	public:
		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}

		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin() const
		{
			return const_reverse_iterator(end());
		}

		const_reverse_iterator rend() const
		{
			return const_reverse_iterator(begin());
		}
	public:
		list()
		{
			empty_init();
		}
		template <class InputIterator>
		list(InputIterator first, InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		list(const list<T>& lt)
		{
			empty_init();
			list<T> tmpList = list(lt.begin(), lt.end());
			swap(tmpList);
		}
		list(int n, const T& val = T())
		{
			empty_init();
			int i = 0;
			while(i++ < n)
			{
				push_back(val);
			}
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}
		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		void swap(list<T>& lt)
		{
			::swap(_head, lt._head);
		}

		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		void push_back(const T& val)
		{
			insert(end(), val);
		}
		void pop_front()
		{
			erase(begin());
		}
		void pop_back()
		{
			erase(-end());
		}
		iterator insert(iterator pos, const T& val)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(val);

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

			return iterator(newnode);
		}
		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;

			return iterator(next);
		}

		T& front()
		{
			return *(begin());
		}
		T& back()
		{
			return *(--end());
		}
		const T& front() const
		{
			return *(begin());
		}
		const T& back() const
		{
			return *(--end());
		}

		bool empty() const
		{
			return _head->_next == _head;
		}
		size_t size() const
		{
			size_t count = 0;
			list<T>::const_iterator it = begin();
			while (it != end())
			{
				++it;
				++count;
			}
			return count;
		}
	private:
		Node* _head;
	};
}

list 与 vector 的对比

vector 与 list 都是重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不尽相同,其主要不同如下:

vector list
底层结构 动态顺序表,一段连续空间 带头结点的双向循环链表
随机访问 支持随机访问 不支持随机访问,访问某个元素的效率是O(N)
插入和删除 任意位置插入删除效率低,时间复杂度是O(N);插入时可能还需要增容,导致效率更低 任意位置插入删除效率高,时间复杂度是O(1)
空间利用率 底层为连续空间,不容易造成内存碎片,缓存利用率高,空间利用率高 底层动态开辟小节点,缓存利用率低,空间利用率低
迭代器 原生态指针 对原生态指针进行封装
迭代器失效 插入元素时,可能导致扩容,使原来的迭代器失效;删除元素时,当前迭代器会失效 插入元素不会导致迭代器失效,删除元素会导致当前迭代器失效
使用场景 需要高效存储,支持随机访问,不关心插入删除效率 需要大量插入删除操作,不关心随机访问
相关推荐
码匠许师傅37 分钟前
【C++ 面试真题】30. 聊聊 C++ 的互斥锁与读写锁
java·c++·面试
ShineWinsu1 小时前
对于 C++:C++14中从变量模板、泛型 Lambda 到并发与字面量的解析
c++·算法
一木 之林4 小时前
四、STL容器与数据结构
开发语言·数据结构·c++
程与留4 小时前
07_QMainWindow 与应用程序框架——菜单栏、工具栏、状态栏
c++·qt
_Narcissus_5 小时前
二分算法笔记及例题
数据结构·c++·笔记·算法·蓝桥杯·查找·二分算法
青少儿编程课堂7 小时前
用图形化编程做一个“少年探险闯关”小游戏:方向键控制、碰撞检测与多关卡串起完整项目
c++·python·算法·bfs·信息学竞赛
liulilittle7 小时前
长上下文的成本结构与「甜点区间」——从推理引擎的物理约束看 200K/256K/400K
c++·人工智能·ai·llm·注意力·qkv
十五年专注C++开发7 小时前
100w条数据丝滑滚动!Qt Model/View 架构实战(二)
开发语言·c++·qt
skr爱码士8 小时前
06_Qt 常用数据类型与容器:QString、QList、QVector、QMap 的性能与实现分析
开发语言·c++·qt
奶茶树9 小时前
【C++】13. C++11新特性【上】
c语言·开发语言·c++·git·github