list的实现

list介绍

list是stl中的一个容器,它是一个带头双向循环链表的线性结构,有头插,尾插等一系列操作,同时,也保存了迭代器的运用,但因为它存储的数据是指向一个空间的指针,实现起来十分繁琐

具体实现

list的成员变量

 不同于vector,list存储的是一个指针,这个指针中需要有三个数据元素,一个是指向下一个节点的指针,一个是指向上一个节点的指针,还有一个是存储的数据(这个和vector相同),所以list实现时,我们用在list中存储的是一个结构体的指针,结构体的书写放在别处

template<class T>
	struct List_Node
	{
		typedef struct List_Node<T> Node;
		T data;
		Node* next;
		Node* prev;
		List_Node(const T&x=T())
			:data(x)
			, next(nullptr)
			, prev(nullptr)
		{ }
		
	};

 这是一个模板类,可以让存储的数据更加灵活,在c++中,struct和class都是定义类的关键字,二者不同之处在于,struct中的默认限定访问符是puplic,class的默认限定访问符是private,当你试实例化一个类对象时,编译器会调用默认构造函数,如果自己没有写,编译器会自己生成,但编译器生成的默认函数不会处理内置类型,只会处理自定义类型,所以我们最好是自己书写一个构造函数,struct类和class类二者除了默认访问限定符存在差别,其他任何地方都没有差别,所以构造拷贝析构等函数都是可以自己写的。

list的迭代器

    我们目前学习的迭代器有string和vector的的迭代器,认识到了迭代器的便利性,因为string和vector存储数据的方式为数组,它们的数据是存储在一段连续的地址空间上,所以可以用原生指针去实现迭代器会更加方便和容易,但list作为双向循环链表,它的数据是存储在基本不连续的空间上,且每个数据是一个存储最终数据的节点,如果在list中设计重载符号,是无法做到指针自加和自减操作,能做到的仅仅是返回节点指针,无法让节点指针自己进行额外操作,如自加自减等,所以我们需要对节点指针封装。

template<class T>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T> self;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

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

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

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}
        T& operator*()
		{
			return _node->_data;
		}

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

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

  这是普通迭代器的实现,它支持对指定空间存储的数据进行读写操作,也支持一系列的指针相关操作,解引用等等,我们其实可以发现,迭代器是模拟指针,但它的所有操作在表面上和原生指针的形式别无二致,但它实际上会多出别的动作,我们看不到它内部的动作,也不关心它的内部动作,我们需要的仅仅是它能和指针一样,这就是封装的好处

1.封装屏蔽底层差异和实现细节

2.提供统一的访问修改遍历方式

  我们使用迭代器是为了更方便得寻找数据,并对数据进行读写操作,但我们有时仅仅希望,我们的数据只进行读,不进行写,为了更好的去读取数据,而百分百不去修饰它,我们往往会用const去修饰,使其不会被修改,const修饰指针变量却有额外的规定

const int*pa//代表pa指向的空间所存储的数据不会被修改,指针本身会被修改,这时的const修饰的是pa所指向的变量;

int*pa const//代表pa这个指针所保存的地址值不会被修改,这个地址所代表的空间中存储的数据是可以修改的,它仅仅是保护了指针变量,并未保护指针变量指向的空间

  我们的迭代器是模拟指针的实现,从实现迭代器的代码来看,它的底层仍是指针,不过在外形式为_list_interator,如果我们单纯得只是在外面加上const,const修饰的会是指针变量,而不是去修饰指针所指向的空间

这是为什么呢?

_list_interator代表的是指针变量,这和你用const直接修饰int是一个道理,它让int变量不被改变,这里就是让指针变量不会被改变,但它原本指向的空间仍然会被改变。

  如果我们仅仅是在原类中再次创建一个相同函数,并对其返回值类型加上const修饰,这也是没有用的,因为构不成函数重载,函数重载是指参数列表和参数排列顺序存在不同,而不是返回值类型不同。 

这时候,我们就需要重新定义一个迭代器类,为const迭代器

cosnt迭代器
template<class T>
	struct __list_const_iterator
	{
		typedef list_node<T> Node;
		typedef __list_const_iterator<T> self;
		Node* _node;

		__list_const_iterator(Node* node)
			:_node(node)
		{}

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

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

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

		
		const T& operator*()
		{
			return _node->_data;
		}

		
		const T* operator->()
		{
			return &_node->_data;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

  对迭代器的实现,代码量不大也不小,我们将它们当中两个不同的迭代器来使用,以应对不同的场景,但也可以省略,我们的迭代器是一个模板类,模板类的实例化参数不同,它们生成的模板类就是不同的模板,所以对普通迭代器,我们可以传一些参数,对cosnt迭代器,又传另外一些参数,以此来获得两个不同的迭代器

template<class T, class Ref, class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;

		__list_iterator(Node* node)
			:_node(node)
		{}

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

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

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self operator--(int)
		{
			self tmp(*this);
			_node = _node->_prev;

			return tmp;
		}

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

		Ptr operator->()
		{
			return &_node->_data;
		}

		bool operator!=(const self& s)
		{
			return _node != s._node;
		}

		bool operator==(const self& s)
		{
			return _node == s._node;
		}
	};

在上述代码中,我们对原本的迭代器类模板新添加了两个参数,一个是Ref,一个是Ptr,在实际应用中,我们就是靠这两个参数不同,来分化不同的迭代器

interator<T,T&,T*> 这是普通迭代器

interator<T,const T&,const T*> 这是cosnt迭代器

编译器会根据参数列表,实例化出两个不同的迭代器,受累的是编译器。

lsit类的实现

list中有许多函数,如尾插,头插,定点插等等,list因为是双向循环列表结构,它的插入和删除效率十分高效,空间利用率高,它的缺点就是不能随意访问,如果硬要达到vector那样的效果,效率会很低,同时,它的cpu缓存命中率很低,因为cpu读取数据是一段字节一段字节读取的,而list是链表,它的数据存储的空间是不连续的,无法做到读取这段数据的时候缓存下一段数据,而vector可以,所以vector的命中率高于list。

​
template<class T>
	class list
	{
		typedef List_Node<T> Node;
	 public:
		 typedef conductor<T,const T&,const T*> const_conductor;
		 typedef conductor<T, T&, T*> conductor;//模板参数列表不同,所实例化的类也是两个不一样的类,如list<int>和list<double>是两个不同的类
		 void empty_intit()
		 {
			 head = new Node;
			 head->prev = head;
			 head->next = head;
		 }

		list()
		{
			empty_intit();

		}
		void clear()
		{
			conductor it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}
		Node* BuyNode(const T& val)
		{
			Node* newnode = new Node;
			newnode->data = val;
			newnode->next = newnode;
			newnode->prev = newnode;
			return newnode;
		}
		void push_back(const T& val)
		{
			Node* tail = head->prev;
			Node* newnode = BuyNode(val);
			tail->next = newnode;
			newnode->prev = tail;
			newnode->next = head;
			head->prev = newnode;
			size++;
		}
		void pop_back()
		{
			Node* prev = head->prev->prev;
			Node* tail = head->prev;
			prev->next = head;
			head->prev = prev;
			delete tail;
			tail = nullptr;
			size--;
		}
		void insert(Node*pos,T& val = T())
		{
			Node* prev = pos->prev;
			Node* newnode = BuyNode(val);
			prev->next = newnode;
			newnode->prev = prev;
			pos->prev = newnode;
			newnode->next = pos;
			size++;
		}
		conductor earse(Node* pos)
		{
			Node* prev = pos->prev;
			Node* next = pos->next;
			prev->next = next;
			next->prev = prev;
			delete pos;
			size--;
			return next;
			
		}
		void push_front(const T& val)
		{
			insert(head->next, val);
		}
		void pop_front(Node* pos)
		{
			earse(head->next);
		}
		const_conductor begin()const
		 {
			return const_conductor(head);
		 }
		const_conductor end()const
	    {
			return const_conductor(head->prev);
	    }
		conductor begin()
		{
			return conductor(head->next);
		}
		conductor end()
		{
			return conductor(head->prev);
		}
	  private:
		  Node*head=nullptr;
		  int size = 0;
	};

​

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值