C++List基础功能模拟实现

List 

之前所讲到的vector是模拟顺序表,而和顺序表互补的则是链表,所以List模拟的就是链表的结构。

在之前数据结构阶段我们知道链表其实有两个部分组成,一个是单节点的结构,另一个则是链接它们的结构,所以我们依旧使用老方法,定义两个结构体。

template <class T>
	class list_node
	{
		T _data;
		list_node* _next;
		list_node* _prev;

		list_node(const T& x =T())
			:_data(x)
			,_next(nullptr)
			,_prev(nullptr)
		{

		}
	};
template <class T>
	class list
	{
     private:
		node* _head;
		size_t _size;

	};

在list的基本功能前,我们还需要一些准备来辅助,但是链表和顺序表不同,它无法通过下标和[]来访问内容,所以链表的迭代器就有些麻烦,这需要我们再定义一个结构来实现。

template <class T,class Ref,class Ptr>//我们不能因为const和非const而写两个结构,那太繁琐了
	struct __list_iterator
	{
        //所以我们采用模版的形式 如果是非const 那么它的参数就是T T& T*
		typedef list_node<T> node;
        //如果是const 那么它的参数就是 T const T&和const T*
		typedef list_iterator<T, Ref, Ptr> self;
        //开节点
		node* _node;

        //构造函数
		__list_iterator(node* node)
			:_node(node)
		{

		}
        //++--我们都不希望它的内容被改变 所以我们用const版本的
		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;
		}
        
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
		bool operator==(const self& s)
		{
			return _node == s._node;
		}
        //我们此时并不知道它是什么类型,但是它会通过模版实例化确认类型
        //它会根据我们传的参数自动匹配是不是const
		Ref operator*()
		{
			return _node->_data;
		}

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

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

这里需要注意的是,我们想实现const类型的迭代器,并不是直接const iterator就可以的,因为如果这么写,我们反而是限制了迭代器不能修改,那么我们的++--都将无法实现,所以我们的const应该是要加在类型前面的,而我们把这一步交给了使用者,他传的什么类型const我们就用它实例化出来。

迭代器都解决了,那么之后的功能实现就很简单了。

        //首先咱先把我们定义的结构体取个别名
        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 iterator(_head->_next);
		}

		iterator end()
		{
			return _head->prev;
		} 
        //const版本
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end() const
		{
			return const_iterator(_head);
		}
        //初始化一个头结点,因为我们是双向带头循环链表
		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			_stze = 0;
		}
        //因为咱是链表只能一个个删
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);

			}
		}
        //构造函数
		list()
		{
			empty_init();

		}
        //析构函数
		~list()
		{
            //通过调用上面的clear清理完数据
			clear();
            //再把头结点释放
			delete _head;
			_head = nullptr;
		}
        //拷贝构造
	    list(const list<T>& it)
		{   
            //这里其实不存在浅拷贝的问题,因为咱节点都是现开的,确实只需要拷贝个数据就可以了
			empty_init();
			for (auto e : it)
			{
				push_back(e);
			}

		}

那么上面的基本运行没有问题之后我们就可以来实现它的功能了。

void swap(list<T>& it)
		{
            //直接使用系统自带的交换即可
			std::swap(_head, it._head);
			std::swap(_size, it._size);
		}
        
		list<int>& operator=(const list<T>& it)
		{
            
			swap(it); 
			return *this;
		}
		 
		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;*/
            

            //直接使用insert简单省事
			insert(end(), x);
		}
        

        //头删头插尾删 其实只需要看insert和erase就好
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front(const T& x)
		{
			erase(begin());
		}

		void pop_back(const T& x)
		{
			erase(--end());
		}

		iterator insert(iterator pos const T& val)
		{    

            //和链表插入大同小异 无非是改改前后的指向然后链接上新节点
			node* cur = pos->_node;
			node* newnode = new node(x);
			node* prev = cur->_prev;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
            //需要注意的是 我们为了省去计算节点个数 所以加了一个size 没错增加结点需要++size
            //删除结点也需要--size;
			++_size;
            //虽然list插入不会发生迭代器失效,但是我们还是给它加一个返回插入后节点的位置
			return iterator(newnode);
		}
		iterator erase(iterator pos)
		{
            //链表因为插入的特殊,所以不会有迭代器失效的问题,但是删除一样会出现迭代器失效
            //基本操作
			node* cur = pos._node;
			node* prev = cur->_prev;
			node* next = cur->_next; 
			delete cur;
			prev->_next = next;
			prev->_prev = prev;

			--_size;

            //因为迭代器会失效,所以我们得返回个新的给它外部接收更新
			return iterator(next);
		}

        //这个就是返回节点数,而我们写了个size成员 所以简简单单。
		size_t size()
		{
			return _size;
		}

关于list,其实和咱数据结构链表差不多,只不过人家各种功能齐全,不需要我们现场写,而我们写是为了知道它为啥实现,咋实现的,并且知道它底层的一些隐藏点,这样真的写起来用起来能避免出问题,就像list迭代器,如果我们不分析,我们很难想到它是咋实现的,反正咱用还是*和->。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值