容器的概观和分类
- array 数组 、list 链表、tree树 、stack堆栈、queue队列、hash table散列表、set集合、map映射表
- 根据数据在容器中的排列顺序,将上述数据结构分为序列式和关联式两种类型
- SGI STL使用内缩方式来表达基层和衍生层之间的关系
- 衍生不是派生,本质上是一种内含的关系,例如heap内含vector,priority-queue内含heap;stack 和 queue都内含deque;set map multiset multimap 内含一个 RB-tree,hash_x都内含hashtable
- 序列式容器,内部元素都可序但是未必有序。C++本身提供了一个序列式容器array,STL提供了vector、list、deque、priority_queue等
- stack 和 queue都是在deque的基础上改头换面形成的,技术上将其归类为一种 配接器
Vector
- vector类似于array,二者唯一的差别在于 空间的运用的灵活性。
- array是静态空间,一旦配置就不会改变大小,如果存储空间不够需要申请空间进行元素的拷贝,这些操作均需要用户自己来执行,最后还需要释放先前拥有的区间。 具体操作:(配置新的空间 | 数据移动 | 释放先前的内存空间 )
- vector是动态空间,随着元素的加入,内部的实现机制会自行进行内存空间的扩充从而容纳新的元素
- vector的实现技术:关键在于其对于大小的控制和重新配置的时候对于数据的移动的效率。第二次申请的内存空间是先前的两倍
- SGI vector将vector实现于更加底层的<stl_vector.h> ,具体使用的时候直接引入<vector>头文件即可
#include <iostream>
#include <new> //for placement new
#include <cstddef> //for ptrdiff_t size_t
#include <cstdlib> //for exit
#include <climits> //for UINT_MAXnamespace Chy{template <class T>inline T* _allocate(ptrdiff_t size,T*){std::set_new_handler(0);T* tmp = (T*)(::operator new((std::size_t)(size * sizeof (T))));if (tmp == 0){std::cerr << "out of memory" << std::endl;exit(1);}return tmp;}template<class T>inline void _deallocate(T* buffer){::operator delete (buffer);}template<class T1,class T2>inline void _construct(T1 *p,const T2& value){new(p) T1 (value); //没看懂}template <class T>inline void _destroy(T* ptr){ptr->~T();}template <class T>class allocator{public:typedef T value_type;typedef T* pointer;typedef const T* const_pointer;typedef T& reference;typedef const T& const_reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;template<class U>struct rebind{typedef allocator<U>other;};pointer allocate(size_type n,const void * hint = 0){return _allocate((difference_type)n,(pointer)0);}void deallocate(pointer p,size_type n){_deallocate(p);}void construct(pointer p,const T& value){_construct(p,value);}void destroy(pointer p){_destroy(p);}pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size()const{return size_type(UINT_MAX/sizeof (T));}};
}template<class T,class Alloc>
class simple_alloc{
public:static T* allocate(std::size_t n){return 0==n?0:(T*)Alloc::allocate(n * sizeof(T));}static T* allocate(void){return (T*)Alloc::allocate(sizeof (T));}static void deallocate(T* p,size_t n){if (n!=0){Alloc::deallocate(p,n * sizeof(T));}}static void deallocate(T* p){Alloc::deallocate(p,sizeof(T));}
};//如果是copy construction 等同于assignment而且destructor 是 trivial以下就会有效
//如果是POD型别 执行的流程就会跳转到以下函数,这个是通过function template的参数推导机制得到的
template<class ForwardIterator,class Size,class T>
inline ForwardIterator __uninitizlized_fill_n_aux(ForwardIterator first,Size n,const T&x){return fill_n(first,n,x); //交给高阶函数执行
}struct __true_type{};
struct __false_type{};template<class T>
struct __type_traits {typedef __true_type this_dummy_member_must_be_first;typedef __false_type has_trivial_default_constructor;typedef __false_type has_trivial_copy_constructor;typedef __false_type has_trivial_assignment_constructor;typedef __false_type has_trivial_destructor;typedef __false_type is_POD_type;
};//函数的逻辑是
//首先萃取出 迭代器first的value type,然后判断这个型别是否是POD类型
template<class ForwardIterator,class Size,class T,class T1>
inline ForwardIterator __uninitizlized_fill_n(ForwardIterator first,Size n,const T&x,T1*){//以下使用的是__type_traits<T1>::is_POD_type is _PODtypedef typename __type_traits<T1>::is_POD_type is_POD;return __uninitizlized_fill_n_aux(first,n,x,is_POD());
}template<class ForwardIterator,class Size,class T>
ForwardIterator uninitialized_fill_n(ForwardIterator first,Size n,const T&x){return __uninitizlized_fill_n(first,n,x,value_type(first));//使用value_type()判断first的value type
}//alloc 是SGI STL默认的空间配置器
template <class T,class Alloc>
class vector{
public://vector的嵌套型别定义typedef T value_type;typedef value_type * pointer;typedef value_type * iterator;typedef value_type & reference;typedef std::size_t size_type;typedef ptrdiff_t difference_type;
protected://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 deallocate(){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 - begin());}bool empty(){return begin() == end();}reference operator[] (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(){Chy::allocator<T>::destroy(start,finish); //全局函数deallocate(); //vector的成员函数}reference front(){return *begin();} //第一个元素reference back(){return *(end()-1);} //最后一个元素void push_back(const T& x){if (finish != end_of_storage){Chy::allocator<T>::construct(finish,x);++finish;} else{insert_aux(end(),x);// vector的成员函数}}void pop_back(){ //将最尾端的元素取出--finish;Chy::allocator<T>::destroy(finish);}iterator erase(iterator position){ //清除某个位置上的元素if (position+1 != end()){std::copy(position+1,finish,position); //后续元素向前移动 进行元素的覆盖}--finish;Chy::allocator<T>::destroy(finish);return position;}void resize(size_type new_size,const T& x){if (new_size < size()){erase(begin()+new_size,end());} else{std::inserter(end(),new_size - size(),x);}}void resize(size_type new_size){resize(new_size,T());}void clear(){erase(begin(),end());}
protected://配置空间并填满内容iterator allocate_and_fill(size_type n,const T&x){iterator result = data_allocator ::allocate(n);uninitialized_fill_n(result,n,x); //全局函数return result;}
};
vector迭代器
- vector维护的是一个连续的线性空间,所以不论其元素的的型别是怎样,普通的指针都可以作为vector的迭代器从而满足所有的必要的条件
- 因为vector迭代器所需要的操作,比如 operator* operator-> operator++ operator-- operator+ operator- operator+= operator-= 是普通指针天生就具备的能力
- 普通指针支持随机存取,正因为普通指针具备这样的能力,所以vector提供的是random Access iterators
- vector的迭代器是普通的指针
- 例子
std::vector<int>::iterator i_vite; //i_vite的型别本质上是int*
std::vector<Shape>::iterator s_vite; //s_vite的型别本质上是Shape*
Vector数据结构
- 数据结构很简单:线性连续空间
- 使用start和finish指向配置的连续空间中的已经使用的范围区间
- 使用迭代器end_of_storage指向的是整个一块连续的空间(包含备用空间)的尾端
iterator start; //表示目前使用空间的头部iterator finish; //表示目前使用空间的尾部iterator end_of_storage; //表示目前可用空间的尾部
- vector分配的内存空间实际上是大于客户端申请的内存空间,主要是为了防止以后内存的扩充,这个便是容量的概念。
- 即容量的概念 永远大于或者等于其大小,一旦容量等于大小,即是满载。
- 使用三个迭代器,完成 vector的首尾标定、使用空间的大小、容量、空容器的判断、注标[]的运算子、最前端的元素 和最末尾的元素等等
Vector的构造和内存管理
- //vector缺省使用alloc作为空间配置器,并据此另外定义了data_allocator,为的是更方便的以元素大小作为配置单位
- typedef simple_alloc<value_type,Alloc>data_allocator; 所以data_allocator::allocate(n) ; 表示配置n个元素的空间
- vector提供了很多的构造器
- uninitialized_fill_n 根据第一个参数的特性(使用type_traits 进行类型推导),决定使用fill_n() 还是通过反复调用 construct() 来完成任务
- 使用push_back()将元素插入到vector的尾端的时候,函数检查是否存在备用的空间,如果有直接在备用空间进行元素的构造,并且调整迭代器finish使得vector变大
- 如果没有备用空间就需要进行元素的扩充(配置 、移动数据、释放空间)
void push_back(const T& x){if (finish != end_of_storage){ //还存在备用空间Chy::allocator<T>::construct(finish,x); //直接进行元素的构造++finish; //调整水位高度} else{ //无备用空间insert_aux(end(),x);// vector的成员函数}}
//insert_aux 伪代码
template<class T,class Alloc>
void vector<T,Alloc>::insert_aux(iterator position, const T &x) {if (finish != end_of_storage){ //还有备用空间//在备用空间起始处构造一个元素,并使用vector最后一个元素数值为其初始化Chy::allocator<T>::construct(finish,*(finish-1));//调整水位++finish;T x_copy = x;copy_backward(position,finish-2,finish-1);*position = x_copy;} else{ //无备用空间const size_type old_size = size();const size_type len = old_size != 0 ? 2*old_size:1;//配置原则://如果原大小为0 则配置1 (单位是 元素的大小)//如果原大小不为0 配置内存大小是先前的两倍//前半段用于放置先前的数据,后半段用于存储新的数据iterator new_start = data_allocator::allocate(len); //实际配置内存iterator new_finish = new_start;try{//进行先前元素的拷贝,将原vector的内容拷贝到新的vectornew_finish = unintialized_copy(start,position,new_start);//为新的元素设定初始数值 xChy::allocator<T>::construct(new_finish,x);//调整水位++new_finish;//将原vector的备用空间中的内容也忠实拷贝过来new_finish = unintialized_copy(position,finish,new_finish);}catch (){//commit or rollbackChy::allocator<T>::destroy(new_start,new_finish);data_allocator::allocate(new_start,len);throw ;}//析构并且释放原有的vectorChy::allocator<T>::destroy(begin(),end());deallocate();//调整迭代器 指向新的vectorstart = new_start;finish = new_finish;end_of_storage = new_start + len;}
}
- 动态增加大小 不是在现有的内存空间后面接入新的空间 ( 无法保证原有空间之后是否还尚有可以配置的空间 )
- 需要在背的地方申请空间
- 因此一旦造成空间的重新配置,指向原有vector的所有迭代器就会失效了
void pop_back(){ //将最尾端的元素取出--finish; //将尾端标记往前移动一格,表示放弃尾端元素Chy::allocator<T>::destroy(finish);}
void pop_back(){ //将最尾端的元素取出--finish; //将尾端标记往前移动一格,表示放弃尾端元素Chy::allocator<T>::destroy(finish);}//清除[first ,last)内部所有元素iterator erase(iterator first,iterator last){iterator i = copy(last,finish,first);Chy::allocator<T>::destroy(i,finish);finish = finish - (last - first);return first;}//清除某个特定位置上的元素iterator erase(iterator position){ //清除某个位置上的元素if (position+1 != end()){std::copy(position+1,finish,position); //后续元素向前移动 进行元素的覆盖}--finish;Chy::allocator<T>::destroy(finish);return position;}void clear(){erase(begin(),end());}
- 这一块还不是很清晰 需要进一步完善 insert(position,n,x)