【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)
空间利用率 底层为连续空间,不容易造成内存碎片,缓存利用率高,空间利用率高 底层动态开辟小节点,缓存利用率低,空间利用率低
迭代器 原生态指针 对原生态指针进行封装
迭代器失效 插入元素时,可能导致扩容,使原来的迭代器失效;删除元素时,当前迭代器会失效 插入元素不会导致迭代器失效,删除元素会导致当前迭代器失效
使用场景 需要高效存储,支持随机访问,不关心插入删除效率 需要大量插入删除操作,不关心随机访问
相关推荐
卷卷的小趴菜学编程23 分钟前
c++之多态
c语言·开发语言·c++·面试·visual studio code
OopspoO1 小时前
C++ 标准库——函数对象和函数适配器
c++
kyle~1 小时前
thread---基本使用和常见错误
开发语言·c++·算法
攻城狮7号1 小时前
【第三节】C++设计模式(创建型模式)-单例模式
c++·单例模式·设计模式
oioihoii2 小时前
深入理解 C++17 的缓存行接口
java·c++·缓存
大白的编程日记.2 小时前
【C++笔记】C+11深度剖析(三)
c语言·开发语言·c++
老菜鸡mou2 小时前
[OD E 100] 生成哈夫曼树
数据结构·c++
别NULL3 小时前
机试题——编辑器
c++·算法
和光同尘@3 小时前
56. 合并区间 (LeetCode 热题 100)
c语言·开发语言·数据结构·c++·算法·leetcode·职场和发展
zh路西法3 小时前
【C++委托与事件】函数指针,回调机制,事件式编程与松耦合的设计模式(上)
开发语言·c++·观察者模式·设计模式