C++ list模拟实现

list的底层是带头双向循环的链表,它可以存放任意数据,因此需要用模板。它功能上的实现其实很简单,但是迭代器是一个比较重要的点,它不是单纯的一个指针,而是一个自定义类型。

一、list类

list的成员有两个,第一个是自义定类型指针,只不过它的类型是一个经过typedef的结点类型,这个结点类型有三个成员,指向下一个结点的指针和指向上一个结点的指针,和存放的值。第二个是一个整形的size,用来记录当前链表数据个数。



namespace vae
{
template <class T>
//list结点类     
   struct list_node    //struct成员默认是公有,方便访问
  {
     list_node<T>* _next;  //类模板之后类名不是类型
     list_node<T>* _prev;
     T _val;
     
//构造函数
     list_node(const T& val = T())
           :_next(nullptr)
           ,_prev(nullptr)
           ,_val(val)
         {}
  } 
//list类
template <class T>
   class list
  {
    
    typedef list_node<T> Node;
    public:
     
         
 
    private:
         //成员
          Node* _head;
          size_t size;
  }
}

二、迭代器类

list的迭代器可不可以用指针呢?显然是不可以的,vector和string可以用指针做迭代器是因为它们的底层是数组,数组它是一段连续的空间,++就可以到下一个的位置,*解引用就可以得到它的值,它天生就可以做迭代器。

但是这里可不可以呢?list的底层是带头双向循环,它的空间是不连续的,++之后谁知道它到哪个位置去了,*解引用能得到它的值吗?不能,所以运算符重载就应该上场了。

 迭代器它的成员是一个结点指针,它有三个模板参数,对照着下面这段代码看就可以理解。

 typedef _list_iterator<T,T&,T*> iterator;
typedef _list_iterator<T, const T&,const T*> const_iterator;

第二个模板参数是用来解决const迭代器和普通迭代器的问题,这样避免了代码冗余。

第三个模板参数是给->运算符重载用的,比如说我们可能有这样的场景。

 

 我们可以看到在上面的代码中迭代器是访问不了的,为什么呢?因为这个地方list存放的是一个个A对象,解引用之后拿到的是A这个结构体,如果有流插入流提取运算符重载是可以的,可是没有,因此在这个地方我们只能用.来访问,但是我们知道,迭代器是指针,那我们直接用箭头访问不就可以了吗?因此我们需要重载箭头,

 但是我们看箭头的运算符重载,我们会发现一个问题it->调用的是运算符重载,它返回的是一个指针,因此指针还要加一个箭头才能访问_a1,所以应该是这样的it->->_a1,但是编译器进行了优化,这样写实在太难看了。

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

list迭代器类完整代码:

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)
		{}
        //运算符重载
		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->_next;
			return tmp;
		}

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

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

三、list类中迭代器 

        typedef _list_iterator<T,T&,T*> iterator;
		typedef _list_iterator<T, const T&,const T*> const_iterator;
           //迭代器
        iterator begin()
		{
           //return iterator(_head->next);
			return _head->_next; //这个地方是隐式类型的转换,生成一个匿名对象
		}
		iterator end()
		{
			return _head;
		}
		const_iterator begin() const
		{
			
			return _head->_next;
		}

		const_iterator end() const
		{
			return _head;
			
		}

_head是头结点,因此begin就是返回_head的下一个结点,end就是返回_head。我们来看一下list的迭代器使用。

	vae::list<int>::iterator it = lt2.begin();
		while (it != lt2.end())
		{
			cout << *it << endl;
			it++;
		}

list的迭代器使用虽然看上去和vector和string的迭代器使用一样,但是底层大不一样,这个地方

it = lt2.begin(),这是调用拷贝构造,而下面基本全都是调用我们实现的迭代器类里面的运算符重载。

四、pos位置插入删除

 1、插入

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

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

			newnode->_next = cur;
			cur->_prev = newnode;
			_size++;

			return newnode;
		}

list的插入非常简单,new一个结点然后改变前后结点链接关系,再让size++即可。 

 2、删除



		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;
		}

删除先保存一下删除结点的前后结点,然后改链接关系,在delete掉pos结点,然后size--,注意要返回当前结点的下一个结点,也就是next结点。

五、尾插头插尾删头删

	   //尾插
       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());
		}

这也没什么说的好吧,直接复用插入删除。

六、清理数据和交换函数

		void clear()
		{
			iterator it = begin();
			
			while (it != end())
			{
				it = erase(it);
				
			}
			_size = 0;
		}

清理数据直接erase就行,list清数据连带空间也要释放掉。 

	void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

直接用库函数交换就ok。

七、当前数据个数和判空

        size_t size()const
		{
			return _size;
		}

		bool empty()const
		{
			return  _head->_next == _head;
		}

八、赋值运算符重载

	list<T>& operator=(list<T> lt)
		{
			swap(lt);

			return *this;
		}

不解释,和list、string一个道理好吧。

九、构造和拷贝构造和析构

//构造
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}
//拷贝构造
		list(const list<T>& lt)
		{
			list();

			for (auto& e : lt)
			{
				push_back(e);
			}
		}
//析构
        ~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}
        

这个地方拷贝构造直接调用构造开好空间,然后用迭代器和尾插依次把值插入进去即可。

析构直接复用clear清掉空间,然后把头结点释放在置为空。

十、list模拟完整代码

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>

namespace vae
{
	template<class T>

	struct list_node
	{
		list_node<T>* _next;
		list_node<T>* _prev;
		T _val;

		list_node(const T& val = T())
			:_next(nullptr)
			, _prev(nullptr)
			, _val(val)
		{}
	};

	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)
		{}

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

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

		Self operator++(T)
		{
			Self tmp(*this);

			_node = _node->_next;
			return tmp;
		}

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

		Self operator--(T)
		{
			Self tmp(*this);

			_node = _node->_next;
			return tmp;
		}

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

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

	
	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;

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

		const_iterator end() const
		{
			return _head;
			
		}

		void empty_init()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

			_size = 0;
		}

		list()
		{
			empty_init();
		}

		list(const list<T>& lt)
		{
			empty_init();

			for (auto& e : lt)
			{
				push_back(e);
			}
		}

		void swap(list<T>& lt)
		{
			std::swap(_head, lt._head);
			std::swap(_size, lt._size);
		}

		list<T>& operator=(list<T> lt)
		{
			swap(lt);

			return *this;
		}

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}
		
		void clear()
		{
			iterator it = begin();
			
			while (it != end())
			{
				it = erase(it);
				
			}
			_size = 0;
		}

		void push_back(const T& x)
		{
			/*Node* tail = _head->_prev;
			Node* newnode = new Node(x);

			tail->_next = newnode;
			newnode->_prev = tail;

			newnode->_next = _head;
			_head->_prev = newnode;
			_size++;*/
			insert(end(), x);
		}

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

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

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

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

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

			newnode->_next = cur;
			cur->_prev = newnode;
			_size++;

			return 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;
			_size--;

			return next;
		}

		size_t size()const
		{
			return _size;
		}

		bool empty()const
		{
			return  _head->_next == _head;
		}

	private:
		Node* _head;
		size_t _size;
	};

	void list_test1()
	{
		list<int> li;
		li.push_back(1);
		li.push_back(2);
		li.push_back(3);
		li.push_back(4);

		list<int>::iterator it= li.begin();
		
		while (it != li.end())
		{
			(*it)++;
			cout << *it << " ";
			++it;
		}
		cout << endl;
	}
	void list_test2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
		lt.push_front(6);
		lt.push_front(7);
		lt.push_front(8);
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		lt.pop_back();
		lt.pop_front();
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;

		list<int>lt2(lt);
		for (auto e : lt2)
		{
			cout << e << " ";
		}
		cout << endl;

		lt2.pop_back();
		lt = lt2;
		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
		vae::list<int>::iterator it = lt2.begin();
		while (it != lt2.end())
		{
			cout << *it << endl;
			it++;
		}
	}

}

int main()
{
	vae::list_test2();
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值