走进STL - 序列式容器(常用篇)

时间:2022-07-23
本文章向大家介绍走进STL - 序列式容器(常用篇),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

在序列式容器的大家庭里,比较常用的还是vector和list。 本篇就重点讲这两个容器的实现。

1、vector

文字表诉的部分文章开头的那篇博客基本讲完了,我们直接来看vector的定义摘要。 哈哈,先把工具包都备齐。然后看代码:

1.1 vector类设计
/*alloc是SGI STL的空间配置器*/

template <class T,class Alloc = alloc>	//模板偏特化,这个工具包我没准备
class vector
{
//以下这一块属于迭代器使用
	public :
	typedef T 			value_type;
	typedef value_type* pointer;
	typedef value_type* iterator;
	typedef value_type& reference;
	typedef	size_t 		size_type;
	typedef	ptrdiff_t 	difference_type;	

	//以下simple_alloc属于SGI STL 空间配置器
	typedef simple_alloc<value_type,Alloc> data_allocator;
	iterator start;	//表示目前使用的空间的头
	iterator finish;//表示目前使用的空间的尾
	iterator end_of_storage;	//表示目前可用的空间的尾

	void insert_aux(iterator position,const T& x);
	void deacllocate()
	{
		if(start)
			data_allocator::deallocate(start,end_of_storage - start);
	}
	void fill_initialize(size_type n,const T& value)
	{
		start = allocate_and_fill(n,value);
		finish = start + n;
		end_of_storage = finish;
	}
//切记,别问,先往下看
	public:
	iterator begin(){return start;}
	iterator end(){return finish;}
	size_type size() const{return size_type(end() - begin());}
	size_type capacity() const{return size_type(end_of_storage - end();}
	bool empty() const {return end_of_storage == end();}
	reference operate[](size_type n){return *(begin() + n);}

	vector():start(0),finish(0),end_of_storage(0){}
	vector(size_type n,const T& value){fill_initialize(n,value);}
	vector(int n,const T& value){fill_initialize(n,value);}
	vector(long n,const T& value){fill_initialize(n,value);}
	explicit vector(size_type n){fill_initialize(n,T());}

	~vector()
	{
		destroy(start,finish);
		deallocate();
	}	

	reference front(){return *begin();}
	reference back(){return *end();}

	void push_back(const T& x)
	{
		if(finish !=end_of_storage)
		{
			construct(finish,x);
			++finish;
		}
		else
			insert_aux(end(),x);	
	}

	void pop_back()
	{
		--finish;
		destroy(finish);
	}

//从这里可以看出是直接覆盖原位置数据,然后回收最后一个节点
	iterator erase(iterator position)
	{
		if(position +1 != end())
			copy(position + 1,finish,position);	//这个函数属于算法	将后续元素前移
		--finish;
		destroy(finish);
		return position;	
	}	

	void resize(size_type new_size,const T& x)
	{
		if(new_size<size())
			erase(begin()+new_size,end());
		else
			insert(end(),new_size - size(),x);
	}
	
	void resize(size_type new_size){resize(new_size,T());}
	void clear(){erase(begin(),end());}

	protect:
	//配置空间并填满
	iterator allocate_and_fill(size_type n,const T& x)
	{
		iterator result = data_allocator::allocate(n);
		uninitialized_fill_n(result,n,x);	//内存配置函数,不过那篇太长了,我就吧un-这一块给咔嚓了
		return result;
	}
}	
1.2 vector其他

源码之前,了无秘密。我还能补充啥?还能补充的在文章开头那篇关于vector的文章里面也都说完了。。。

2、list

关于list的节点设计、迭代器设计、数据结构,题头那篇已经详尽。 list增删的坑,可以看:走近STL - 填上List删除的大坑

好,我们也是直接看源码吧,其实和上面的vector有很多相通之处的。

2.1 节点设计
//为了完整性,我们还是先看一下节点设计
template <class T>
struct __list_node
{
	typedef void * void_pointer;
	void_pointer prev;
	void_pointer next;
	T data;
}	//显然,这是一个双向链表,图就不再画一遍了
2.2 迭代器设计
//list的迭代器不像vector,普通指针是不行的

//以下是list迭代器的设计

template <class T,class Ref,class Ptr>
struct __list_iterator
{
	typedef	T 		value_type;
	typedef Ptr 	pointer;
	typedef Ref 	reference;
	typedef size_t 	size_type;
	typedef ptrdiff_t 		difference_type;
	
	typedef __list_node<T>* link_type;
	typedef __list_iterator<T,T&,T*> 	iterator;
	typedef __list_iterator<T,Ref,Ptr> 	self;
	typedef	bidirectional_iterator_tag 	iterator_category;
	
	
	link_type node;	//用于指向内部节点
	
	//构造函数
	__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}
	
	reference operator*() const {return (*node).data;}
	pointer operator->() const {return &(operator*());}

	self& operator++()	//对节点进退操作,vector中并没有重载这些
	{
		node = (link_type)((*node).next);
		return *this;
	}
	self& operator++(int)
	{
		self tmp = *this;
		++*this;
		return tmp;
	}
	self& operator--()
	{
		node = (link_type)((*node).prev);
		return *this;
	}
	self& operator++(int)
	{
		self tmp = *this;
		++*this;
		return tmp;
	}
}
2.3 数据结构

SGI list 不止是一个双向链表,它还是环状的。所以它只要一个指针便可以遍历整个链表。

如果让指针node指向刻意置于尾端的一个空白节点,node便能符合STL“前闭后开”的原则。

至于和vector重复的方法像begin()这些的就不再提一遍了。

我们来看看list特有的方法:

//首先争议比较大的删除函数先看
//都没必要争议,源码之前,了无秘密
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;

	//然后把那个节点回收
	destory_node(position.node);
	return iterator(next_node);	//	看清楚返回值
}

算了,算法部分放到后面算法区写吧 这篇就到这里啦