C++中List的实现

list是一个双向链表结构

每一个节点(_node)都包含_prev节点,_next节点和数据_date,这说明_node的类型将会是一个结构体

list的end和begin是相互链接的,我们将end移动前面便于理解,如图

_node结构体:

struct _list_node
{ 
       _list_node*_prev;
       _list_node*_next;
       T _date;

      _list_node(T val=T())   //默认构造函数,将成员初始化
       :_prev(nullptr)
       ,_next(nullptr)
       ,_date(val)
    {}
}

迭代器的成员:

list的访问不同于vectoroperator[]重载函数下标直接访问,我们需要获取节点的指针进行访问,此外还要实现指针的++解引用!=,等函数,就将这些封装成迭代器_list_iterator,其成员对象就是Node*_node,也就是节点的地址,其他的成员函数实现访问功能。

为了后面const与非const变化使用,我们需要一个模板

1.模板的使用

	 template<class T ,class Ref, class Ptr>
     typedef _list_iterator<T, Ref, Ptr> self;
	 typedef _list_iterator<T,T&,T*> iterator;
	 typedef _list_iterator<T,const T&,const T*> const_iterator;

当我们创建了一个迭代器类型的变量 it 

1.1

iterator it;

此时的 it 调用的就是第3行的模板,当用Ref返回it中存储的值引用(it._node->_date)的时候,Ref对应的就是T&,允许通过改变返回值改变节点中的_date。

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

当用Ptr返回it中存储的_node(该节点的地址)(it._node->)的时候,Ptr对应的就是T*。

1.2

const_iterator  it;

此时的 it 调用的就是第4行的模板,当用Ref返回it中存储的值引用(it._node->_date)的时候,Ref对应的就是const T&,不允许通过改变返回值改变节点中的_date。

当用Ptr返回it中存储的_node(该节点的地址)(it._node->)的时候,Ptr对应的就是const T*。

总结:我们只要在创建迭代器变量的时候确定是const_iterator还是iterator类型,后序若是不小心改变了const修饰对象的引用,系统也会报错。

2._list_iterator成员函数

namespace GuYu
{
	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 = nullptr)
			:_node(node)
		{}

		//  前置++
		self& operator++()   //   先++,再返回,返回的是加过后的迭代器,不需要创建临时变量,所以可以引用返回
		{
			_node = _node->_next;
			return *this;       //  *this=it,it中存储的_node已经变为下一个_node(_node->_next)
		}

		//  后置++
		self operator++(T)
		{
			_list_iterator tem(this->_node);
			_node = _node->_next;
			return tem;                 //  有拷贝函数的参与 (开辟空间,将tmp的值拷贝到新开辟的空间中)  //  出函数,tem销毁
		}

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

		//  后置--
		self operator--(T)
		{
			_list_iterator tem(this->_node);
			_node = _node->_prev;
			return tem;                   //  有拷贝函数的参与 
		}

		Ref operator*()           // 传了this指针(&it)
		{
			return _node->_date;  //  this->_node->_next
		}

		bool operator!=(const _list_iterator& lis)
		{
			return _node != lis._node;
		}
	};

}

3.list的实现

3.1 list默认构造函数

list()
{
	_head = new Node();
	_head->_prev = _head;
	_head->_next = _head;
}

3.2 list的begin()函数

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

在return的时候发生了一次创建临时变量,等效于以下写法

		const_iterator begin()const
		{
			iterator it(_head->_next);    //  可以替换  但是所有调用共享一个 it 影响后序结果
			return it;
		}

3.3 push_back()

		void push_back(T val)
		{
			Node* newnode = new Node(val);
			Node* nodetail = _head->_prev;      // 找到最后一个节点
			_head->_prev = newnode;
			newnode->_next = _head;
			newnode->_prev = nodetail;
			nodetail->_next = newnode;
		}

3.4 基本框架

namespace GuYu
{
	template<class T>
  //  各种获取值(_date)和节点(_node)在struct _list_iterate 已经完成,在struct list用不到Ref Ptr         
	struct list
	{
		typedef _list_node<T> Node;
		typedef _list_iterator<T, T&, T*> iterator;
		typedef _list_iterator<T, const T&, const T*> const_iterator;
		list()
		{
			_head = new Node();
			_head->_prev = _head;
			_head->_next = _head;
		}
        
        ~list()
		{
			this->clear();
			_head = nullptr;

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

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

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

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



		void push_back(T val)
		{
			Node* newnode = new Node(val);
			Node* nodetail = _head->_prev;      // 找到最后一个节点
			_head->_prev = newnode;
			newnode->_next = _head;
			newnode->_prev = nodetail;
			nodetail->_next = newnode;
		}

		list(const list<T>& lis)
		{
			_head = new Node();
			_head->_prev = _head;
			_head->_next = _head;
			const_iterator it = lis.begin();  
			while (it != lis.end())
			{
				this->push_back(*it);
				it++;
			}

			//for (auto e : lis)
			//{
			//	this->push_back(e);
			//}
		}
		
		iterator erase(iterator it)  //  返回删除迭代器的下一个迭代器,也就是_node->_next
		{
			Node* cur = it._node;
			//获取下一节点的迭代器
			iterator itnext(cur->_next);
			// 将前后node链接
			cur->_prev->_next = cur->_next;
			cur->_next->_prev = cur->_prev;
			// 删除it位置的node
			delete it._node;
			return itnext;
		}


		void clear()
		{
			iterator it = this->begin();
			while (it != this->end())
			{
				erase(it++);       //  调用了++重载函数   it先++,返回的是原来的it  //  返回的是临时对象的值    用erase传值而不是引用
			}
			//最后会只剩下_head ( end() )
		}

		list<T>& operator=(const list<T>& lis) //  传的list要与迭代器it保持一致的const  , 否则找不到合适的begin()和end()
		{
			if (this == &lis)
			{
				return *this;
			}
			//首先要清空this 中的数据,只保留_head用于尾插
			this->clear();
			const_iterator it = lis.begin();
			while (it != lis.end())
			{
				this->push_back(*it);
				it++;
			}
		}


//  为什么不传引用  1. 避免意外发生,将数据改变   2.STL的一致性,erase splice 都采用迭代器传值  3.迭代器通常很小,传值的开销与传引用的开销几乎相同
		iterator insert(iterator it,T val)   
		{
			Node* newnode = new Node(val); //创建新节点,并赋值_date
			Node* cur = it._node;        
			Node* prev = cur->_prev;
            //  将新建的节点插入 cur 和 prev 两个节点之间
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
			return iterator(newnode);   // 传值返回插入的新节点
		}

		void print()const
		{
			const_iterator it = this->begin();
			while (it != this->end())
			{
				cout << *it << endl;
				++it;           //  ++it 减少空间拷贝
			}
		}
	private:
		Node* _head;
	};

}

4.测试结果

5. list的优点缺点

优点:任意位置插入删除数据效率高,o(1)

缺点:不支持随机访问

list 可以与vector相辅相成,将list的数据拷贝到vector中,再支持排序二分查找堆算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值