list容器的源码以及实例

本文深入探讨了C++标准模板库(STL)中List容器的内部实现细节,包括其双向链表结构、迭代器设计、节点管理和核心算法如插入、删除、合并、排序等。通过代码示例展示了List的操作原理及其实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下是list的node结点设计

template <class T>
struct __list_node{
	typedef void* void_pointer;//型别是void *,但是实际上可以设为__list_node<T>*
	void_pointer prev;
	void_pointer next;
	T data;};

list不再向vector一样拿普通指针作为迭代器,因为其结点不保证在存储空间中连续存在,list迭代器必须有能力指向list的结点,并且有能力进行正确的递增/递减/取值/成员存取等操作.递增指的是指向下一个结点,递减指的是指向上一个结点
list有一个重要性质:插入操作(insert)和接合操作(splice)都不会造成原有的list迭代器失效.erase也只是造成指向的那个元素失效,不会对其他迭代器产生影响.

template <class T,class Ref,class Ptr>
struct __list_iterator{
	typedef __list_iterator<T,T&,T*> iterator;
	typedef __list_iterator<T,Ref,Ptr> self;
	typedef bidirectional_iterator_tag iterator_category;
	typedef T value_type;
	typedef Ptr pointer;
	typedef Ref reference;
	typedef __list_node<T> *link_type;
	typedef size_t size_type;
	typedef ptrdiff_t difference_type;
	link_type node;//迭代器内部当然要有一个普通指针,指向list 结点
	//constructor 
	__list_iterator(link_type x):node(x){}
	__list_iterator(){}
	__list_iterator(const iterator& x):node(x.node){}
	bool operator==(const self &x) const {return node==x.node;}
	bool operator!=(const self& x) const{return node!=x.node;}
	//以下对迭代器取值(dereference),取的是节点的数据值
	reference operator*()const{return (*node).data;}
	//以下是迭代器的成员存取,运算子的标准做法
	pointer operator ->()const{return &(operator*());}
	//对迭代器累加1,就是前进一个节点
	self & operator++(){
	node=(link_type)((*node).next);
	return *this;
}
	self operator++(int){//后向加
	self tmp= *this;
	++*this;
	return tump;
	
}
//对迭代器减一,就是后退一个结点
self &operator--(){
	node=(link_type)((*node).prev);//指向前一个结点
	return *this;
}
self operator--(int){
	self tmp=*this;
	--*this;
	return tmp;
}
};

SGI list不仅是一个双向链表,而且还是一个环装双向链表 所以它只需要一个指针,便可以完整表现整个链表
让node可以指向放在尾部的一个空白结点

下面是几个函数
iterator begin(){return (link_type)((*node).next);}//因为是环状,那么最后一个空白结点的下一个结点就是头结点
iterator end(){return node;}//那么node结点自然而然就是尾部结点

下面是一个list实例:

#include<list>
#include<iostream>
#include<algorithm>
using namespace std;
int main(){
	int i;
	list <int> ilist;
	cout<<"size="<<ilist.size()<<endl;//size=o
	ilist.push_back(0);
	ilist.push_back(1);
	ilist.push_back(2);
	ilist.push_back(3);
	ilist.push_back(4);
	cout<<"size="<<ilist.size()<<endl;//size=5
	list<int>::iterator ite;
	for(ite=ilist.begin();ite!=ilist.end();++ite)
		cout<<*ite<<' ';
	cout<<endl;
	ite=find(ilist.begin(),ilist.end(),3);
	//if(ite != 0)//这个地方暂时有点问题 后期再来处理
		ilist.insert(ite,99);
	cout<<"size="<<ilist.size()<<endl;//size=6
	cout<<*ite<<endl;//3
	for(ite=ilist.begin();ite!=ilist.end();++ite)
		cout<<*ite<<' ';
	cout<<endl;
	ite=find(ilist.begin(),ilist.end(),1);
	//if(ite!=0)
		cout<<*(ilist.erase(ite))<<endl;//2
	for(ite=ilist.begin();ite!=ilist.end();++ite)
		cout<<*ite<<' ';
	cout<<endl;
	
}

list缺省使用alloc作为空间配置器,并据此另外定义了一个list_node_allocator为的是更方便的以结点大小为配置单位

//专属空间配置器,每次配置一个大小
typedef simple_alloc<list_node,Alloc>list_node_allocator;

于是list_node_allocatior(n)表示配置n个结点的空间
下面四个函数用来配置,释放,构造,销毁一个结点

protected:
		//配置一个结点并返回
		link_type get_node(){return list_node_allocator::allocate();}
		//释放一个结点
		void put_node(link_type p){list_node_allocator::deallocate(p);}
		//产生(配置并构造)一个结点,带有元素值
			link_type create_node(const T&x){
			link_type p=get_node();
			construct(&p->data,x);//全局函数 构造析构的基本工具
			return p;
			//销毁(析构并释放)一个结点
			void destory_node(link_type p){
			destory(&p->data);//全局函数
				put_node(p);
}
		}

list提供许多constructors,其中一个是default constructor,允许我们不指定任何参数做出一个空的list出来

pulic :
	list(){empty_initialize();}
protected:
		void empty_initialize(){
			node=get_node();//配置一个结点空间,令node指向它
			node->next=node;//令node头尾都指向自己 ,不设元素值
			node_prev=node;
}

当我们调用push_back()的时候,是将新元素插入到list的尾端,此函数内部调用insert():

void push_back(const T& x){insert(end(),x);}

insert()函数的目的是:在迭代器position位置插入一个新结点,它的内容是x

iterator  insert(iterator position,const T& x){
	link_type tmp=create_node(x);//差生一个结点(设置内容为x)
	//调整双向指针,是tmp能够插入进去
	tmp->next=position.node;
	tmp->prev=position.node->prev;
	(link_type(position.node->prev))->next=tmp;
	position.node->prev=tmp;
	return tmp;}

由于list不像vector那样有可能在空间不足时重新配置,数据移动操作,所以插入前的迭代器在插入操作之后都仍然有效.

//插入一个结作为头结点
void push_front(const T& x){insert(begin(),x);}
//插入一个结点作为尾结点
void push_back(const T &x){insert(end(),x);}
//移除迭代器position所指结点
iterator erase(iterator position){
	link_type next_node=link_type(position.node->next);
	link_type prev_node=link_type(position.node->prev);
	prev_node->next=next_node;
	next_node->prev=prev_node;
	destorty_node(position.node);
	return iterator(next_node);
}
//移除头结点
void pop_front(){erase(begin());}
//移除尾结点
void pop_back(){
	iterator tmp=end();
	erase(--tmp);//因为end()位置是一个空节点 所以需要前移一个结点
}
//清楚所有节点,整个链表
template <class T,class Alloc>
void list<T,Alloc>::clear(){
	link_type cur=(link_type)node->next;//这是头结点
	while(cur!=node){//遍历每一个结点
		link_type tmp=cur;
		cur=(link_type)cur->next;
		destory_node(tmp);

}
	//回复node的原始状态 都指向尾结点
	node->next=node;
	node->prev=node;
}
//将数值为valued的全部移除
template<class T,class Alloc>
void list<T,Alloc>::remove(const T& value){
	iterator first=begin();
	iterator last=end();
	while(first!=last){//遍历每一个结点
		iterator next=first;
		++next;
		if(*first==value) erase(first);
		first=next;
}
}
//移除数值相同的连续元素 注意:只有"连续而相同的元素"才会被移除一个
template <class T,class Alloc>
void list<T,Alloc>::unique(){
	iterator first=begin();
	iterator last=end();
	if(first==last) return;//空链表 什么都不必做
	iterator next=first;
	while(++next!=last){//遍历每一个结点
		if(*first==*next)
			erase(next);
		else
			first=next;//调整指针
			next=first;//修正区段范围
} 
}

list 内部提供有一个所谓迁徙操作

protected:
	//将[first,last)内的所有元素移动到position之前
	void transfer(iterator position,iterator first,iterator last){
		if(position!=last){
		(*(link_type((*last.node).prev))).next=position.node;//1
		(*(link_type((*first.node).prev))).next=last;//2
		(*(link_type((*position.node).prev))).next=first.node;//3
		link_type tmp=link_type((*position.node).prev);//4
		(*position.node).prev=(*last.node).prev;//5
		(*last.node).prev=(*first.node).prev;//6
		(*first.node).prev=tmp;//7
	}
}

这个过程有点复杂
在这里插入图片描述
上述的transfer并非是公开的接口,list提供的公开接口是(splice):将某个连续范围的元素从一个list移动到另一个(或同一个)list的某个定点
下面是一些函数的具体实现,

//为了提供各种接口弹性,list<T>::splice有许多版本
public:
	//将x接合于position位置之前,x必须不同于*this
	void splice(iterator position,list &x){
	if(!x.empty())
		transfer(position,x.begin(),x.ned());
}
	//将i所指元素接合于position位置所指元素之前,position和list可可指向同一个list
	void splice(iterator position,list&,iterator i){
	iterator j=i;
	++j;
	if(position==i||position==j) return ;
	transfer(position,i,j);
}
//将[first,last)内的所有元素接合于position所指位置之前
//positionhe[first,last)可指向同一个list
//但position不能位于[firslast)之内
void splice(iterator position,list &,iterator first,iterator last){
	if(first!=last)
		transfer(position,first,last);
}
//一下是merge(),reverse(),sort()的源代码,有了transfer()在手,这些操作就会变得简单
//merge()将x合并到*this上  两个list都必须经过递增排序
template <class T,class Alloc>
void list<T,Alloc>::merge(list<T,Alloc>&x){
	iterator first1=begin();
	iterator last1=end();
	iterator first2=x.begin();
	iterator last2=x.end();
	//注意前提是两个list都经过递增排序
	while(first1!=last1&&first2!=last2)
		if(*first2<*first1){
			iterator next=first2;
			transfer(first1,first2,++next);
			first2=next;
}
	else
		++first1;
		if(first2!=last2) transfer(last1,first2,last2);
}
//reverse()将*this内容逆向重置
template<class T,class Alloc>
void list<T,Alloc>::reverse(){
	//以下判断,如果是空列表或者仅有一个元素,那么就不进行任何操作
	//使用size()==0||size()==1来判断,虽然也可以,但是比较缓慢
	if(node->next==node||link_type(node->next)->next==node)
		return ;//此时不进行任何操作
	iterator first=begin();
	++first;
	while(first!=end()){
		iterator old=first;
		++first;
		transfer(begin(),old,first);
}
}
//list 不能使用STL的sort(),必须使用自己的sort()成员函数
//因为STL算法sort()只接受RandomAccessIterator
template<class T,class Alloc>
void list<T,Alloc>::sort(){
	//以下判断如果是空列表或者仅有一个元素,就不进行任何操作
	//与上面同理
	if(node->next==node||link_type(node->next)->next==node)
		return;
	//一些新的lists作为中介数据存放区
	list<T,Alloc>carry;
	list<T,Alloc>counter[64];
	int fill=0;
	while(!empty()){
		carry.splice(carry.begin(),*this,begin());
		int i=0;
		while(i<fill&&!counter[i].empty()){
			counter[i].merge(carry);
			carry.swap(counter[i++]);
}
		carry.swap(counter[i]);
		if(i==fill) ++fill;
}
	for(int i=1;i<fill;++i)
		counter[i].merge(counter[i-1]);
		swap(counter[fill-1]);
}


最后这个sort()函数使用的快速排序,但是没太看明白,后期回来整理一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值