deque概述
vector是一种单向开口的连续线性空间,而deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作,如图为deque的示意图:
deque和vector 的最大差异:
- deque允许常数时间对头端进行元素的插入和删除操作
- deque没有所谓的容量观念,因为它是动态地以分段连续空间组合而成的,随时可以增加一段新的空间并链接起来,换句话说,像vector那样因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间的事情在deque是不会发生的,因此deque也没有必要提供空间保留(reverse)的功能
虽然deque也提供Random Access Iterator,但它的迭代器并不是普通指针,其复杂度和vector也不一样,这样必然会影响各个运算层面,因此,除非必要,我们应该尽可能选择vector而非deque。
对deque进行的排序操作,为了最高的效率,可将deque先完整复制到一个vector中,对vector调用sort算法后再复制回deque。
deque的中控器
deque逻辑上是连续空间,但实际上是分段连续的,而非完全连续。deque是由一段一段的定量连续空间构成,一旦有必要在deque的前段或尾端增加新空间,便配置一段定量连续空间,然后串接在整个deque的头端或者尾端。因此,deque最大的任务就是在这些分段的定量连续空间上,维护其整体连续的假象,并提供随机存取的借口,避开了“重新配置,复制,释放”的轮回,代价则是复杂的迭代器架构。
deque采用一块所谓的map(不是STL的map容器)作为主控,这里所谓的map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向一段(较大的)连续空间,称为缓冲区。缓冲区才是deque的储存空间主体,SGI STL允许我们指定缓冲区大小,默认值0表示将使用512bytes缓冲区。
namespace TinySTL{
template<typename T,typename Alloc = allocator<T>,size_t BufSize = 0>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
private:
typedef pointer* map_pointer;
private:
map_pointer map;//指向map,map是块连续空间,其内的
// 每个元素都是一个指针(称为节点),
// 指向一块缓冲区
size_type map_size;//map的容量,多少个指针,多少个缓冲区
};
}
由上可知,map其实是一个T**,也就是说它是一个指针,所指之物也是指针,或者可以看成是指针指向型别为T的连续的空间。
deque的迭代器
deue是分段连续空间,维护其“整体连续”假象的任务,落在了迭代器operator++和operator–两个运算符身上。
deque迭代器必须能够指出分段连续空间(缓冲区)在哪里,其次它必须能够判断自己是否已经处于其所在缓冲区的边缘,如果是,一旦前进或后退就必须跳跃到下一个或上一个缓冲区,为了能够正确跳跃,deque必须随时掌握管控中心(map)。
namespace TinySTL{
//全局函数
inline size_t deque_buf_size(size_t n,size_t sz){
//如果指定n不为0,则表示buffer size为n,也就是用户自定义大小
//否则要视T而定,如果sizeof(T)大于512个字节,则缓冲区大小就为1
//小于512个字节,就设为512/sz的大小
return n!=0 ? n : (sz < 512 ? size_t(512/sz):size_t(1));
}
template<typename T>
class deque_iterator{
public:
typedef deque_iterator<T> iterator;
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T** map_pointer;
typedef deque_iterator self;
//迭代器需要4个指针来完成操作
T* cur; //指向当前缓冲区当前的元素位置
T* first; //指向当前缓冲区的头部
T* last; //指向当前缓冲区的尾部(含备用空间)
map_pointer node; //指向主控中心当前的缓冲区的位置
static size_t buff_size(){
return deque_buf_size(BufSize,sizeof(T));
}
};
}
因此需要用4个指针来表示一个deque的iterator。
而在deque需要保持两个迭代器,start和finish,迭代器start内的cur指针指向第一个缓冲区的第一个元素,迭代器finish内的cur指针指向最后一个缓冲区的最后元素的下一个位置。
为了实现deque连续的假象,迭代器中对各种指针运算如加、减、前进、后退最关键在于:一旦行进时遇到缓冲区边缘,要特别当心,视前进或后退而定,可能需要调用set_node来跳一个缓冲区,用set_node来调整iterator中的node指针,调整其在map中的位置:
//
// Created by Administrator on 2020/3/31.
//
#ifndef MYTINYSTLNEW_DEQUEITERATOR_H
#define MYTINYSTLNEW_DEQUEITERATOR_H
#include <cstddef>
#include "Iterator.h"
namespace TinySTL{
//全局函数
inline size_t deque_buf_size(size_t n,size_t sz){
//如果指定n不为0,则表示buffer size为n,也就是用户自定义大小
//否则要视T而定,如果sizeof(T)大于512个字节,则缓冲区大小就为1
//小于512个字节,就设为512/sz的大小
return n!=0 ? n : (sz < 512 ? size_t(512/sz):size_t(1));
}
template<typename T>
class deque_iterator{
public:
typedef deque_iterator<T> iterator;
typedef random_access_iterator_tag iterator_category;
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef ptrdiff_t difference_type;
typedef size_t size_type;
typedef T** map_pointer;
typedef deque_iterator<T> self;
//迭代器需要4个指针来完成操作
T* cur; //指向当前缓冲区当前的元素位置
T* first; //指向当前缓冲区的头部
T* last; //指向当前缓冲区的尾部(含备用空间)
map_pointer node; //指向主控中心当前的缓冲区的位置
static size_t buff_size(){
return deque_buf_size(BufSize,sizeof(T));
}
void set_node(map_pointer new_node){
node = new_node;
first = *new_node;
last = first + difference_type(buff_size());
}
reference operator*()const{return *cur;}
pointer operator->()const{return &(operator*());}
difference_type operator-(const self& x)const{
//相减,计算是否跨越node
return difference_type((node - x.node - 1)*buff_size() + (cur-first) + (x.last - x.cur));
}
self& operator++(){
//要判断是否越界
++cur;
if(cur == last){
node = set_node(node+1);
node = first;
}
return *this;
}
self operator++(int){
self temp = *this;
++*this;
return temp;
}
self& operator--(){
if(cur == first){
set_node(node-1);
cur = last;
}
--cur;
return *this;
}
self operator--(int){
self temp = *this;
--*this;
return temp;
}
//以下实现随机存取,迭代器可以直接跳跃n个距离
//这些函数需要测试一下
self& operator+=(difference_type n){
difference_type all = (cur-first)+n;
if(n >= 0) {
if (all >= buff_size()) {
//如果超过了一个缓冲区的大小,则要跨缓冲区
difference_type node_num = all / buff_size();
difference_type node_left = all - node_num * buff_size();
node = set_node(node + node_num);
cur = first + node_left;
} else {
cur = cur + n;
}
}else{
if(all < 0){
//如果超过了一个缓冲区的大小,则要跨缓冲区
difference_type node_num = (-all) / buff_size() + 1;
difference_type node_left = (-all) - (node_num-1) * buff_size();
node = set_node(node - node_num);
cur = last - node_left - 1;
}else{
cur = cur - n;
}
}
}
self& operator-=(difference_type n){
return *this += -n;
}
//不用&
self operator+(difference_type n)const {
self temp = *this;
return temp += n;
}
self operaor-(difference_type n)const {
self temp = *this;
return temp -= n;
}
//以下实现随机存取,迭代器可以直接跳跃n个距离
//这个函数看看是怎么用的
reference operator[] (difference_type n){
return *(*this + n);
}
bool operator==(const self& x)const{
return cur == x.cur;
}
bool operator!=(const self& x)const{
return !(*this == x);
}
bool operator<(const self& x)const{
return node == x.node ? (cur < x.cur) : (node < x.node);
}
};
}
#endif //MYTINYSTLNEW_DEQUEITERATOR_H
deque的数据结构
deque除了维护一个指向map的指针外,还有维护两个迭代器,分别是start和finish,也就是为了满足STL前闭后开的原则,这两个迭代器分别指向第一个缓冲区的第一个元素和最后一个缓冲区的最后一个元素的下一个位置。此外,我们也必须记住map的大小,一旦map所提供的结点不足,需要对其进行扩容,而扩容时候一般是把原map放到新map的中间位置,方便push_front和push_back.
//
// Created by Administrator on 2020/3/31.
//
#ifndef MYTINYSTLNEW_DEQUE_H
#define MYTINYSTLNEW_DEQUE_H
#include "Allocator.h"
#include "DequeIterator.h"
namespace TinySTL{
template<typename T,typename Alloc = allocator<T>,size_t BufSize = 0>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef deque_iterator<T,BufSize> iterator;
private:
typedef pointer* map_pointer;
map_pointer map; //指向map,map是块连续空间,其内的
// 每个元素都是一个指针(称为节点),
// 指向一块缓冲区
size_type map_size; //map的容量,多少个指针,多少个缓冲区
//维护两个iterator
iterator start; //指向第一个缓冲区的第一个元素
iterator finish; //指向最后一个缓冲区的最后一个元素
public:
//接着可以根据迭代器来实现功能了
reference begin(){return start;}
reference end(){return finish;}
reference front(){return *start;}
reference back(){
return *(finish - 1);
}
reference operator[](const size_t& n){
//迭代器中已经实现了[]重载,可以直接调用了
return start[difference_type(n)];
}
size_type size()const{return finish - start;}
//这个函数不知道是干嘛的
size_type max_size()const{return size_type(-1);}
bool empty()const{return finish == start;}
};
}
#endif //MYTINYSTLNEW_DEQUE_H
deque的构造与内存管理
deque自行定义了两个空间配置器,一个用来配置map空间,另一个用来配置缓冲区空间,其构造函数如下:
注意:deque初始状态,在什么都没有的情况下要保证有一个缓冲区,因此后面clear也是要保证clear后要有一个缓冲区存在。
都是用的allocator,内部使用的是alloc,但allocator实现了对个数的分配,map分配的是T**,缓冲区分配的是*.
typedef allocator<pointer> map_allocator;//用来分配map空间
typedef allocator<T> data_allocator;//用来分配缓冲区空间
构造函数的实现,基本步骤是:
根据大小分配map空间->对map中的结点分配缓冲区->然后一一赋值即可。下面剖析一下分配空间的函数如下:
//构造函数
//deque<int> a;
deque(){
//初始状态什么都没有的话也要保证有一个缓冲区构成一种空的状态
map = map_allocator::allocate(1);
map_size = 1;
*map = data_allocator::allocate(buff_size());
start.set_node(map);
finish.set_node(map);
start.cur = start.first;
finish.cur = finish.first;
//空的状态是finish.cur == start.cur就表示为空了
}
//deque<int> a(20,9) deque<int> b(20)
deque(int n,const value_type& x = T()){
fill_initialize(n,x);
}
// deque<int> a = {1,2,3,4,5,6,7,8};
deque(std::initializer_list<T> l):deque(l.begin(),l.end()){}
//deque<int> a(b.begin(),b.end())
template<typename InputIterator>
deque(InputIterator first,InputIterator last):deque(){
//换一种写法,针对这堆数据,先定位到终点,一半push_front,一半push_back
//std::cout<<last - first<<std::endl;
/*difference_type mid = (last - first) / 2;
for(auto iter = first+mid;iter != first-1;--iter)
(*this).push_front(*iter);
for(auto iter = first+mid+1;iter != last;++iter)
(*this).push_back(*iter);
*/
//先创建map和对应的node
difference_type n = last - first;
create_map_and_nodes(n);
for(map_pointer cur = start.node;cur < finish.node;++cur){
//这样写会有问题,因为first+buff_size(),之后会变成下一个缓冲区的首个元素的地址
//内部copy调用memcpy会调用last - first时候出现问题
//TinySTL::uninitialized_copy(first,)
TinySTL::uninitialized_copy(first,first+buff_size(),*cur);
first = first + buff_size();
}
TinySTL::uninitialized_copy(first,last,finish.first);
}
//deque<int> a(b)
deque(deque<T>& x):deque(x.begin(),x.end()){}
//deque<int> a(std::move(b))
deque(deque<T>&& x){
//直接赋值
map = x.map;
map_size = x.map_size;
start = x.start;
finish = x.finish;
x.map = 0;
x.map_size = 0;
x.finish = x.start;
}
在deque(const size_type& n,const value_type& x)中调用的是一下两个函数,首先我们需要调用create_map_and_nodes分配map并且对map中的node分配空间,并且初始化start,finish两个迭代器,之后再fill_initialize中对已分配的空间进行初始化.*
map分配的是,最少为8个空间,不然就是所需的结点数+2(首尾各留一各空的node)
分配好空间之后就可以调用uninitialized_fill来进行初始化了,其他构造函数的原理类似,关键是要计算好多大的map,并且我们为map前后都预留出一定的空间,只填充中间部分!deque()无参构造函数比较特别,我们规定最原始状态也要为map保留一个node,也就是原始状态下也要分配一个缓冲区,然后设置finish = start – 内部> finish.cur = start.cur即可实现空的效果。
//填充并且初始化
void fill_initialize(size_type n,const value_type& x){
//产生deque的结构
create_map_and_nodes(n);
//然后为每个缓冲区设定初值
map_pointer cur;
for(cur = start.node;cur<finish.node;++cur){
uninitialized_fill(*cur,*cur+buff_size(),x);
}
uninitialized_fill(finish.first,finish.cur,x);
}
void create_map_and_nodes(size_type n) {
//首先计算出需要多少个结点,然后allocate节点数
//然后为没一个结点allocate一段空间
size_type node_nums = n / buff_size() + 1;
//定义map管理的节点数,最少为8,最大为node_nums+2
map_size = TinySTL::max(size_type(8), node_nums + 2);
//然后就可以调用allocate分配内存了
map = map_allocator::allocate(map_size);
//我们配置出来的map一般是比需要的多两个结点
//然后我们在头和尾留出一个结点,方便push_front和push_back
map_pointer nstart = map + (map_size - node_nums)/2;
map_pointer nfinish = nstart + node_nums - 1;
map_pointer cur;
try{
//为map内的每个结点分配一段连续空间
for(cur = nstart;cur <= nfinish;++cur){
*cur = data_allocator::allocate(buff_size());
}
}catch(...){
//暂未实现分配失败回收内存
throw "allocate error!";
}
//set_node函数会顺带设置迭代器的first和last
start.set_node(nstart);
finish.set_node(nfinish);
//cur需要单独设置
start.cur = start.first;
//注意,finish的cur要指向尾元素的下一个位置
finish.cur = finish.first + n % buff_size();
}
push_back(),push_front(),pop_back(),pop_front(),clear()
注意push_back,push_front在尚有备用空间的时候可以直接构造元素,但是如果备用空间用完了,则需要对下一个或上一个node进行分配空间,再构造元素的操作,但是有可能map空间也不足,这时候就需要重新选定新的map空间,进行分配空间,拷贝旧空间,回收旧空间的操作了。
而pop_front,pop_back之后如果析构的是当前node的最后一个元素,则需要回收该缓冲区,然后更新对应的start和finish迭代器
而clear函数需要保证最后留有一个缓冲区,当start和finish的node不同的时候,可以先回收掉其中间的缓冲区,然后回收finish.node,留下start.node即可,但是注意更新finish = start才能使状态变为空。
void push_back(const value_type& x){
if(finish.cur != finish.last -1){
//最后缓冲区尚有一个以上的备用空间
construct(finish.cur,x);
++finish.cur;
//std::cout<<"Process push_back_1"<<std::endl;
}else{
//如果最后缓冲区没有或者只剩下一个备用空间
push_back_aux(x);
//std::cout<<"Process push_back_2"<<std::endl;
}
}
void push_front(const value_type& x){
if(start.cur != start.first){
//还有空间,则构造
construct(start.cur-1,x);
--start.cur;
//std::cout<<"Process push_front_1"<<std::endl;
}else{
push_front_aux(x);
//std::cout<<"Process push_front_2"<<std::endl;
}
}
void pop_back(){
if(finish.cur != finish.first){
--finish.cur;
destroy(finish.cur);
}else{
//回收最后一个node
pop_back_aux();
}
}
void pop_front(){
if(start.cur != start.last-1){
destroy(start.cur);
++start.cur;
}else{
//回收第一个node
pop_front_aux();
}
}
void clear(){
//注意:deque最初的状态至少是一个缓冲区,因此clear析构掉所有元素
//但是仍然要保持有一个缓冲区
//首先将出去start和finish后中间的缓冲区都给析构并且回收掉
map_pointer cur = start.node;
//用!=可能会跳过finish,要用<号
for(++cur;cur < finish.node;++cur){
//把中间的缓冲区析构并且回收掉
destroy(*cur,*cur+buff_size());
data_allocator::deallocate(*cur,buff_size());
}
//然后判断首尾是否属于同一个
if(start.node == finish.node){
//如果首尾属于同一个,那就只析构元素,不回收缓冲区
//调整迭代器指向即可
destroy(start.cur,finish.cur);
}else{
//如果不是同一个,那就回收finish.node,保留start.node
destroy(finish.first,finish.cur);
data_allocator::deallocate(finish.first,buff_size());
destroy(start.cur,start.last);
}
//调整迭代器
finish = start;
}
在push_back或者push_front的时候,当备用空间不足的时候,需要分配新的node,当node不足的时候,需要更新map,通过reserve_map_at_back()和reserve_map_at_front()判断是否出现node不足的时候,注意是否需要+1!!!
void push_back_aux(const value_type& x){
//需要先配置一块新的空间,然后才能构造新的元素
value_type x_copy = x;
//std::cout<<"mark1"<<std::endl;
reserve_map_at_back(); //如果满足某种条件,则必须换一个map
//std::cout<<"mark2"<<std::endl;
//分配多一个node
*(finish.node + 1) = data_allocator::allocate(buff_size());
//std::cout<<"mark3"<<std::endl;
//把原来的最后一个元素分配完,然后finish移动到下一个node的开始处
construct(finish.cur,x_copy);
//std::cout<<"mark4"<<std::endl;
finish.set_node(finish.node+1);
finish.cur = finish.first;
}
void push_front_aux(const value_type& x){
//需要先配置一块新的空间,然后才能构造新的元素
value_type x_copy = x;
reserve_map_at_front(); //如果满足某种条件,则必须换一个map
//在前面分配多一个node
*(start.node - 1) = data_allocator::allocate(buff_size());
//移动迭代器
start.set_node(start.node - 1);
start.cur = start.last - 1;
construct(start.cur,x_copy);
}
//map空间不够的时候更新map
void reserve_map_at_back(size_type nodes_to_add = 1){
//计算后端剩余空间是否小于需要的结点容量,小于就要扩容了
if(map_size - (finish.node - map) - 1 < nodes_to_add){
//如果map尾端的结点备用空间不足
//符合以上条件则必须重换一个map
//配置一块更大的map,拷贝原来的,释放旧map
reallocate_map(nodes_to_add,false);
}
}
//map空间不够的时候更新map
void reserve_map_at_front(size_type nodes_to_add = 1){
//计算后端剩余空间是否小于需要的结点容量,小于就要扩容了
if(start.node - map < nodes_to_add){
//如果map尾端的结点备用空间不足
//符合以上条件则必须重换一个map
//配置一块更大的map,拷贝原来的,释放旧map
reallocate_map(nodes_to_add,true);
}
}
如果node不够的话,就更新map
更新map需要判断(该函数用bool值判断需要向前腾空间还是往后腾空间,这将决定新的start的位置)
- 如果当前空间大于两倍的新的想申请的空间,就不需要申请更多了,做适当的调整即可,视新的start在旧start的前后调用copy还是copy_backward!
- 如果当前空间没有大于两倍的新的想申请的空间,就申请一块两倍的空间+2,首尾各多空出来一个node,然后进行搬移
void reallocate_map(size_type nodes_to_add,bool add_at_front){
//用bool变量区分要往前加或是往后加
//如果为true说明前面要腾出空间来,那么计算新的start就要加上nodes_to_add
//如果为false说明后面要腾出空间来
//std::cout<<"process reallocate_map1"<<std::endl;
size_type old_num_nodes = finish.node - start.node + 1;
size_type new_num_nodes = old_num_nodes + nodes_to_add;
map_pointer new_start;
//如果本map比要扩容的两倍还大,就没必要扩容了,适当的移一下位就可以了
//如果不是的话就要重新找map然后复制过去
//std::cout<<"process reallocate_map2"<<std::endl;
if(map_size > 2*new_num_nodes){
std::cout<<"a"<<std::endl;
//计算新的start应该位于哪个位置
//移位到中间,然后根据bool值判断前面留空间还是后面留空间
new_start = map + (map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
if(new_start < start.node) {
//std::cout << "process reallocate_map_copy" << std::endl;
std::copy(start.node, finish.node + 1, new_start);
}else {
//std::cout<<"process reallocate_map_copy_backward"<<std::endl;
std::copy_backward(start.node, finish.node + 1, new_start + old_num_nodes);
}
}else{
//std::cout<<"process reallocate_map_else"<<std::endl;
//std::cout<<"b"<<std::endl;
size_type new_map_size = map_size + max(map_size,nodes_to_add) + 2;
//配置新的空间
map_pointer new_map = map_allocator::allocate(new_map_size);
//std::cout<<"process reallocate_map_else_1"<<std::endl;
new_start = new_map + (new_map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
//把原来的map复制到新的map
std::copy(start.node,finish.node+1,new_start);
//std::cout<<"process reallocate_map_else_2"<<std::endl;
//释放原来的map
map_allocator::deallocate(map,map_size);
//设定新的map和map_size
map = new_map;
map_size = new_map_size;
};
//调整完以后修改迭代器
start.set_node(new_start);
finish.set_node(new_start + old_num_nodes - 1);
//std::cout<<"process reallocate_map"<<std::endl;
}
erase,erase返回erase位置的iterator
erase的原则是判断erase位置前的元素少还是处在erase位置后的元素少,哪边元素少就对那一边的元素进行整体搬移的操作,操作对象是iterator,内部调用的iterator移动的操作符我们都已经重载完成了,调用copy或者copy_backward的时候可以直接使用iterator,搬移完成后对单个元素进行pop_back或者pop_front,对一个范围的话就进行手动回收空间。
//清除pos所指的元素,pos为清除点,返回清除后的迭代器
iterator erase(iterator pos){
iterator next = pos;
++next;
//计算清除点到起始点有多少个元素
difference_type index = pos - start;
if(index < size() / 2){
//说明清楚点前的元素更少,
//因此从前往后覆盖效率更高
//从前往后覆盖调用copy_backward()函数即可
std::copy_backward(start,pos,next);
pop_front();//然后把第一个元素出掉
}else{
//如果后面元素少,从后往前覆盖效率更高
//从后往前调用copy函数
std::copy(next,finish,pos);
pop_back();
}
return start+index;
}
//清除给定范围
iterator erase(iterator first,iterator last){
if(first == start && last == finish){
clear();
return finish;
}
difference_type n = last - first;//清除区间的长度
difference_type elems_before = first - start;//清除区间前面的元素个数
if(elems_before < (size() - n)/2){
//如果前方的元素量比较少,那就从前往后覆盖的效率更高
//从前往后覆盖调用copy_backward函数
//last是删除的最后元素的下一个位置,是不删除的
std::copy_backward(start,first,last);
//然后要对前面的地址进行析构回收了
iterator new_start = start + n;
destroy(start,new_start);
for(map_pointer cur = start.node;cur < new_start.node;++cur)
data_allocator::deallocate(*cur,buff_size());
start = new_start;
} else{
//如果后面的元素更少,则从后面往前面进行覆盖
std::copy(last,finish,first);
//然后把后面的析构掉,析构函数可以直接传递迭代器
//析构函数会根据是否是POD类型就型重载
iterator new_finish = finish - n;
destroy(new_finish,finish);
for(map_pointer cur = new_finish.node+1;cur <= finish.node;++cur)
data_allocator::deallocate(*cur,buff_size());
finish = new_finish;
}
//还是返回的删除后的iterator
return start+elems_before;
}
insert,insert后返回insert位置的iterator
insert单个元素的原则也是看哪一边的元素少,对那一边进行push一个元素,然后进行整体搬移从而腾出一个空间来,放入要插入的元素。
insert多个元素的原则也是哪边元素少,往哪边搬移,要先计算好所需要的结点的数量!
特别注意往前搬移的时候已经多出来一个元素了,因此pos要更新一下!
iterator insert(iterator position,const value_type &x){
if(position.cur == start.cur){
//如果插入点是deque的最前端
push_front(x);
return start;
}else if(position.cur == finish.cur){
push_back(x);
return finish-1;
}else{
return insert_aux(position,x);
}
}
iterator insert_aux(iterator pos,const value_type& x){
difference_type index = pos - start;//插入点前的元素个数
value_type x_copy = x;
if(index < size() / 2){
//如果插入点前的元素比较少,则进行从后往前覆盖效率更高
//一定要好好看好迭代器的变化
//首先push_front(front)后start会更新到前一个位置
//之后移动应该忽略到原来的start
push_front(front());
iterator front1 = start;
++front1;//front1现在指向原来的start的位置
iterator front2 = front1;
++front2;//front2指向start的后一个位置,也就是从这个位置开始进行覆盖
//因为前面扩了一个元素,因此原来的pos位置会提前一个位置
pos = start + index;//这样pos会比之前提前一个位置,因为前面扩了一个位置
iterator pos1 = pos;
++pos1;//找到要移动区间的后一个位置
std::copy(front2,pos1,front1);
}else{
//如果后面的元素值更少,那么就从前往后覆盖腾出一个位置
push_back(back());//首先push_back多一个内容
iterator back1 = finish;//新的finish比原来的后移一个位置
--back1;//得到原来的finish
iterator back2 = back1;//得到
--back2;
pos = start + index;//pos是不变的,但是添加一个元素防止迭代器失效还是更新一下
std::copy_backward(pos,back2,back1);
}
*pos = x_copy;
return pos;//返回插入后的值
}
template<typename InputIterator>
iterator insert(iterator position,InputIterator first,InputIterator last){
if(position == start){
for(--last;first != last;--last){
push_front(*last);
}
push_front(*first);
return start;
}else if(position == finish){
//如果是尾部
for(;first != last;++first){
push_back(*first);
}
return finish-1;
}else{
return insert_aux(position,first,last);
}
}
//指定位置插入n个x
iterator insert(iterator position,const int& n,const value_type& x){
if(position == start){
for(int i=0;i<n;i++)
push_front(x);
return start;
}else if(position == finish){
//如果是尾部
for(int i=0;i<n;i++)
push_back(x);
return finish-1;
}else{
return insert_aux(position,size_type (n),x);
}
}
template<typename InputIterator>
iterator insert_aux(iterator pos,InputIterator first,InputIterator last){
difference_type n = last - first;//获取插入的元素个数
difference_type index = pos - start;
//判断前面元素多还是后面元素多,从少的一边插入
if(index < size() / 2){
//如果前面的元素比较少,则向前移动
//计算除去前面的备用空间
if(n <= start.cur - start.first){
//如果start所在的结点备用空间足够,则直接移位即可
std::cout<<"insert 1"<<std::endl;
iterator new_start = start - n;
std::copy(start,pos,new_start);
pos = new_start + index;
std::copy(first,last,pos);
start = new_start;
}else{
//如果start的备用空间不够,就需要计算还需要多少个结点
//假如去掉start的备用空间还需要少于buff_size()个元素,则需要1个结点
//-1可以保证假设buff_size = 4,假如需要4个元素,其实只需要一个node,-1后会使得其除法得到0,然后+1就是1个node
//假如是8,需要两个那么就会得到7/4 + 1= 2刚好是两个
std::cout<<"insert 1 else"<<std::endl;
size_type need_node_nums = difference_type(n - (start.cur - start.first) - 1) / buff_size() + 1;
reserve_map_at_front(need_node_nums);//不够的话就更新map
//判断前面的node能否满足
//接着就可以开始copy了
//先把需要的node进行allocate
map_pointer cur = start.node - 1;
for(;need_node_nums > 0;--cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后可以直接调用copy函数,从后往前用用copy,从前往后用copy_backward
iterator new_start = start - n;
//先把start - pos(不包括) 拷贝过去
std::copy(start,pos,new_start);
//iterator old_pos = pos;
pos = new_start + index;
std::copy(first,last,pos);
start = new_start;
}
}else{
//如果后面的元素比较少,则向后移动
if(finish.last - finish.cur - 1 >= n){
//如果finish.node的备用空间足够
//则直接复制就可以了,向后复制用copy_backward
std::cout<<"insert 2"<<std::endl;
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
std::copy(first,last,pos);
finish = new_finish;
pos = start + index;
}else{
//计算需要多少个结点
std::cout<<"insert 2else"<<std::endl;
difference_type left = finish.last - finish.cur - 1;
std::cout<<"left:"<<left<<std::endl;
size_type need_node_nums = difference_type (n - left - 1) / buff_size() + 1;
std::cout<<"need_node_nums:"<<need_node_nums<<std::endl;
//查看后面所剩的结点数是否足够
reserve_map_at_back(need_node_nums);
map_pointer cur = finish.node + 1;
for(;need_node_nums > 0;++cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后开始copy
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
std::copy(first,last,pos);
finish = new_finish;
pos = start + index;
}
}
return pos;
}
iterator insert_aux(iterator pos,const size_type& n,const value_type& x){
difference_type index = pos - start;
//判断前面元素多还是后面元素多,从少的一边插入
if(index < size() / 2){
//如果前面的元素比较少,则向前移动
//计算除去前面的备用空间
if(n <= start.cur - start.first){
//如果start所在的结点备用空间足够,则直接移位即可
std::cout<<"insert 1"<<std::endl;
iterator new_start = start - n;
std::copy(start,pos,new_start);
pos = new_start + index;
std::fill_n(pos,n,x);
//std::copy(first,last,pos);
start = new_start;
}else{
//如果start的备用空间不够,就需要计算还需要多少个结点
//假如去掉start的备用空间还需要少于buff_size()个元素,则需要1个结点
//-1可以保证假设buff_size = 4,假如需要4个元素,其实只需要一个node,-1后会使得其除法得到0,然后+1就是1个node
//假如是8,需要两个那么就会得到7/4 + 1= 2刚好是两个
std::cout<<"insert 1 else"<<std::endl;
size_type need_node_nums = difference_type(n - (start.cur - start.first) - 1) / buff_size() + 1;
reserve_map_at_front(need_node_nums);//不够的话就更新map
//判断前面的node能否满足
//接着就可以开始copy了
//先把需要的node进行allocate
map_pointer cur = start.node - 1;
for(;need_node_nums > 0;--cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),x);
}
//然后可以直接调用copy函数,从后往前用用copy,从前往后用copy_backward
iterator new_start = start - n;
//先把start - pos(不包括) 拷贝过去
std::copy(start,pos,new_start);
//iterator old_pos = pos;
pos = new_start + index;
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
start = new_start;
}
}else{
//如果后面的元素比较少,则向后移动
if(finish.last - finish.cur - 1 >= n){
//如果finish.node的备用空间足够
//则直接复制就可以了,向后复制用copy_backward
std::cout<<"insert 2"<<std::endl;
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
finish = new_finish;
pos = start + index;
}else{
//计算需要多少个结点
std::cout<<"insert 2else"<<std::endl;
difference_type left = finish.last - finish.cur - 1;
std::cout<<"left:"<<left<<std::endl;
size_type need_node_nums = difference_type (n - left - 1) / buff_size() + 1;
std::cout<<"need_node_nums:"<<need_node_nums<<std::endl;
//查看后面所剩的结点数是否足够
reserve_map_at_back(need_node_nums);
map_pointer cur = finish.node + 1;
for(;need_node_nums > 0;++cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后开始copy
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
finish = new_finish;
pos = start + index;
}
}
return pos;
}
所有代码
//
// Created by Administrator on 2020/3/31.
//
#ifndef MYTINYSTLNEW_DEQUE_H
#define MYTINYSTLNEW_DEQUE_H
#include <iostream>
#include "Allocator.h"
#include "DequeIterator.h"
#include "Algorithm.h"
#include "../head/UninitializedFunctions.h"
#include <initializer_list>
namespace TinySTL{
template<typename T,typename Alloc = allocator<T>,size_t BufSize = 0>
class deque{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef deque_iterator<T,BufSize> iterator;
private:
typedef pointer* map_pointer;
typedef allocator<pointer> map_allocator;//用来分配map空间
typedef allocator<T> data_allocator;//用来分配缓冲区空间
map_pointer map; //指向map,map是块连续空间,其内的
// 每个元素都是一个指针(称为节点),
// 指向一块缓冲区
size_type map_size; //map的容量,多少个指针,多少个缓冲区
//维护两个iterator
iterator start; //指向第一个缓冲区的第一个元素
iterator finish; //指向最后一个缓冲区的最后一个元素
static size_t buff_size(){
return deque_buf_size(BufSize,sizeof(T));
}
//填充并且初始化
void fill_initialize(size_type n,const value_type& x){
//产生deque的结构
create_map_and_nodes(n);
//然后为每个缓冲区设定初值
map_pointer cur;
for(cur = start.node;cur<finish.node;++cur){
uninitialized_fill(*cur,*cur+buff_size(),x);
}
uninitialized_fill(finish.first,finish.cur,x);
}
void create_map_and_nodes(size_type n) {
//首先计算出需要多少个结点,然后allocate节点数
//然后为没一个结点allocate一段空间
size_type node_nums = n / buff_size() + 1;
//定义map管理的节点数,最少为8,最大为node_nums+2
map_size = TinySTL::max(size_type(8), node_nums + 2);
//然后就可以调用allocate分配内存了
map = map_allocator::allocate(map_size);
//我们配置出来的map一般是比需要的多两个结点
//然后我们在头和尾留出一个结点,方便push_front和push_back
map_pointer nstart = map + (map_size - node_nums)/2;
map_pointer nfinish = nstart + node_nums - 1;
map_pointer cur;
try{
//为map内的每个结点分配一段连续空间
for(cur = nstart;cur <= nfinish;++cur){
*cur = data_allocator::allocate(buff_size());
}
}catch(...){
//暂未实现分配失败回收内存
throw "allocate error!";
}
//set_node函数会顺带设置迭代器的first和last
start.set_node(nstart);
finish.set_node(nfinish);
//cur需要单独设置
start.cur = start.first;
//注意,finish的cur要指向尾元素的下一个位置
finish.cur = finish.first + n % buff_size();
}
void push_back_aux(const value_type& x){
//需要先配置一块新的空间,然后才能构造新的元素
value_type x_copy = x;
//std::cout<<"mark1"<<std::endl;
reserve_map_at_back(); //如果满足某种条件,则必须换一个map
//std::cout<<"mark2"<<std::endl;
//分配多一个node
*(finish.node + 1) = data_allocator::allocate(buff_size());
//std::cout<<"mark3"<<std::endl;
//把原来的最后一个元素分配完,然后finish移动到下一个node的开始处
construct(finish.cur,x_copy);
//std::cout<<"mark4"<<std::endl;
finish.set_node(finish.node+1);
finish.cur = finish.first;
}
void push_front_aux(const value_type& x){
//需要先配置一块新的空间,然后才能构造新的元素
value_type x_copy = x;
reserve_map_at_front(); //如果满足某种条件,则必须换一个map
//在前面分配多一个node
*(start.node - 1) = data_allocator::allocate(buff_size());
//移动迭代器
start.set_node(start.node - 1);
start.cur = start.last - 1;
construct(start.cur,x_copy);
}
//map空间不够的时候更新map
void reserve_map_at_back(size_type nodes_to_add = 1){
//计算后端剩余空间是否小于需要的结点容量,小于就要扩容了
if(map_size - (finish.node - map) - 1 < nodes_to_add){
//如果map尾端的结点备用空间不足
//符合以上条件则必须重换一个map
//配置一块更大的map,拷贝原来的,释放旧map
reallocate_map(nodes_to_add,false);
}
}
//map空间不够的时候更新map
void reserve_map_at_front(size_type nodes_to_add = 1){
//计算后端剩余空间是否小于需要的结点容量,小于就要扩容了
if(start.node - map < nodes_to_add){
//如果map尾端的结点备用空间不足
//符合以上条件则必须重换一个map
//配置一块更大的map,拷贝原来的,释放旧map
reallocate_map(nodes_to_add,true);
}
}
void reallocate_map(size_type nodes_to_add,bool add_at_front){
//用bool变量区分要往前加或是往后加
//如果为true说明前面要腾出空间来,那么计算新的start就要加上nodes_to_add
//如果为false说明后面要腾出空间来
//std::cout<<"process reallocate_map1"<<std::endl;
size_type old_num_nodes = finish.node - start.node + 1;
size_type new_num_nodes = old_num_nodes + nodes_to_add;
map_pointer new_start;
//如果本map比要扩容的两倍还大,就没必要扩容了,适当的移一下位就可以了
//如果不是的话就要重新找map然后复制过去
//std::cout<<"process reallocate_map2"<<std::endl;
if(map_size > 2*new_num_nodes){
std::cout<<"a"<<std::endl;
//计算新的start应该位于哪个位置
//移位到中间,然后根据bool值判断前面留空间还是后面留空间
new_start = map + (map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
if(new_start < start.node) {
//std::cout << "process reallocate_map_copy" << std::endl;
std::copy(start.node, finish.node + 1, new_start);
}else {
//std::cout<<"process reallocate_map_copy_backward"<<std::endl;
std::copy_backward(start.node, finish.node + 1, new_start + old_num_nodes);
}
}else{
//std::cout<<"process reallocate_map_else"<<std::endl;
//std::cout<<"b"<<std::endl;
size_type new_map_size = map_size + max(map_size,nodes_to_add) + 2;
//配置新的空间
map_pointer new_map = map_allocator::allocate(new_map_size);
//std::cout<<"process reallocate_map_else_1"<<std::endl;
new_start = new_map + (new_map_size - new_num_nodes) / 2
+ (add_at_front ? nodes_to_add : 0);
//把原来的map复制到新的map
std::copy(start.node,finish.node+1,new_start);
//std::cout<<"process reallocate_map_else_2"<<std::endl;
//释放原来的map
map_allocator::deallocate(map,map_size);
//设定新的map和map_size
map = new_map;
map_size = new_map_size;
};
//调整完以后修改迭代器
start.set_node(new_start);
finish.set_node(new_start + old_num_nodes - 1);
//std::cout<<"process reallocate_map"<<std::endl;
}
void pop_back_aux(){
//回收最后一个缓冲区
data_allocator::deallocate(finish.first,buff_size());
finish.set_node(finish.node - 1);
finish.cur = finish.last - 1;
destroy(finish.cur);
}
void pop_front_aux(){
//回收第一个缓冲区
destroy(start.cur);
data_allocator::deallocate(start.first,buff_size());
start.set_node(start.node+1);
start.cur = start.first;
}
iterator insert_aux(iterator pos,const value_type& x){
difference_type index = pos - start;//插入点前的元素个数
value_type x_copy = x;
if(index < size() / 2){
//如果插入点前的元素比较少,则进行从后往前覆盖效率更高
//一定要好好看好迭代器的变化
//首先push_front(front)后start会更新到前一个位置
//之后移动应该忽略到原来的start
push_front(front());
iterator front1 = start;
++front1;//front1现在指向原来的start的位置
iterator front2 = front1;
++front2;//front2指向start的后一个位置,也就是从这个位置开始进行覆盖
//因为前面扩了一个元素,因此原来的pos位置会提前一个位置
pos = start + index;//这样pos会比之前提前一个位置,因为前面扩了一个位置
iterator pos1 = pos;
++pos1;//找到要移动区间的后一个位置
std::copy(front2,pos1,front1);
}else{
//如果后面的元素值更少,那么就从前往后覆盖腾出一个位置
push_back(back());//首先push_back多一个内容
iterator back1 = finish;//新的finish比原来的后移一个位置
--back1;//得到原来的finish
iterator back2 = back1;//得到
--back2;
pos = start + index;//pos是不变的,但是添加一个元素防止迭代器失效还是更新一下
std::copy_backward(pos,back2,back1);
}
*pos = x_copy;
return pos;//返回插入后的值
}
template<typename InputIterator>
iterator insert_aux(iterator pos,InputIterator first,InputIterator last){
difference_type n = last - first;//获取插入的元素个数
difference_type index = pos - start;
//判断前面元素多还是后面元素多,从少的一边插入
if(index < size() / 2){
//如果前面的元素比较少,则向前移动
//计算除去前面的备用空间
if(n <= start.cur - start.first){
//如果start所在的结点备用空间足够,则直接移位即可
std::cout<<"insert 1"<<std::endl;
iterator new_start = start - n;
std::copy(start,pos,new_start);
pos = new_start + index;
std::copy(first,last,pos);
start = new_start;
}else{
//如果start的备用空间不够,就需要计算还需要多少个结点
//假如去掉start的备用空间还需要少于buff_size()个元素,则需要1个结点
//-1可以保证假设buff_size = 4,假如需要4个元素,其实只需要一个node,-1后会使得其除法得到0,然后+1就是1个node
//假如是8,需要两个那么就会得到7/4 + 1= 2刚好是两个
std::cout<<"insert 1 else"<<std::endl;
size_type need_node_nums = difference_type(n - (start.cur - start.first) - 1) / buff_size() + 1;
reserve_map_at_front(need_node_nums);//不够的话就更新map
//判断前面的node能否满足
//接着就可以开始copy了
//先把需要的node进行allocate
map_pointer cur = start.node - 1;
for(;need_node_nums > 0;--cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后可以直接调用copy函数,从后往前用用copy,从前往后用copy_backward
iterator new_start = start - n;
//先把start - pos(不包括) 拷贝过去
std::copy(start,pos,new_start);
//iterator old_pos = pos;
pos = new_start + index;
std::copy(first,last,pos);
start = new_start;
}
}else{
//如果后面的元素比较少,则向后移动
if(finish.last - finish.cur - 1 >= n){
//如果finish.node的备用空间足够
//则直接复制就可以了,向后复制用copy_backward
std::cout<<"insert 2"<<std::endl;
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
std::copy(first,last,pos);
finish = new_finish;
pos = start + index;
}else{
//计算需要多少个结点
std::cout<<"insert 2else"<<std::endl;
difference_type left = finish.last - finish.cur - 1;
std::cout<<"left:"<<left<<std::endl;
size_type need_node_nums = difference_type (n - left - 1) / buff_size() + 1;
std::cout<<"need_node_nums:"<<need_node_nums<<std::endl;
//查看后面所剩的结点数是否足够
reserve_map_at_back(need_node_nums);
map_pointer cur = finish.node + 1;
for(;need_node_nums > 0;++cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后开始copy
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
std::copy(first,last,pos);
finish = new_finish;
pos = start + index;
}
}
return pos;
}
iterator insert_aux(iterator pos,const size_type& n,const value_type& x){
difference_type index = pos - start;
//判断前面元素多还是后面元素多,从少的一边插入
if(index < size() / 2){
//如果前面的元素比较少,则向前移动
//计算除去前面的备用空间
if(n <= start.cur - start.first){
//如果start所在的结点备用空间足够,则直接移位即可
std::cout<<"insert 1"<<std::endl;
iterator new_start = start - n;
std::copy(start,pos,new_start);
pos = new_start + index;
std::fill_n(pos,n,x);
//std::copy(first,last,pos);
start = new_start;
}else{
//如果start的备用空间不够,就需要计算还需要多少个结点
//假如去掉start的备用空间还需要少于buff_size()个元素,则需要1个结点
//-1可以保证假设buff_size = 4,假如需要4个元素,其实只需要一个node,-1后会使得其除法得到0,然后+1就是1个node
//假如是8,需要两个那么就会得到7/4 + 1= 2刚好是两个
std::cout<<"insert 1 else"<<std::endl;
size_type need_node_nums = difference_type(n - (start.cur - start.first) - 1) / buff_size() + 1;
reserve_map_at_front(need_node_nums);//不够的话就更新map
//判断前面的node能否满足
//接着就可以开始copy了
//先把需要的node进行allocate
map_pointer cur = start.node - 1;
for(;need_node_nums > 0;--cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),x);
}
//然后可以直接调用copy函数,从后往前用用copy,从前往后用copy_backward
iterator new_start = start - n;
//先把start - pos(不包括) 拷贝过去
std::copy(start,pos,new_start);
//iterator old_pos = pos;
pos = new_start + index;
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
start = new_start;
}
}else{
//如果后面的元素比较少,则向后移动
if(finish.last - finish.cur - 1 >= n){
//如果finish.node的备用空间足够
//则直接复制就可以了,向后复制用copy_backward
std::cout<<"insert 2"<<std::endl;
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
finish = new_finish;
pos = start + index;
}else{
//计算需要多少个结点
std::cout<<"insert 2else"<<std::endl;
difference_type left = finish.last - finish.cur - 1;
std::cout<<"left:"<<left<<std::endl;
size_type need_node_nums = difference_type (n - left - 1) / buff_size() + 1;
std::cout<<"need_node_nums:"<<need_node_nums<<std::endl;
//查看后面所剩的结点数是否足够
reserve_map_at_back(need_node_nums);
map_pointer cur = finish.node + 1;
for(;need_node_nums > 0;++cur,--need_node_nums){
//为每个结点申请空间
*cur = data_allocator::allocate(buff_size());
//顺便填充初值,这样写可以简单一点
TinySTL::uninitialized_fill_n(*cur,buff_size(),*start);
}
//然后开始copy
iterator new_finish = finish + n;
std::copy_backward(pos,finish,new_finish);
//std::copy(first,last,pos);
std::fill_n(pos,n,x);
finish = new_finish;
pos = start + index;
}
}
return pos;
}
public:
//构造函数
//deque<int> a;
deque(){
//初始状态什么都没有的话也要保证有一个缓冲区构成一种空的状态
map = map_allocator::allocate(1);
map_size = 1;
*map = data_allocator::allocate(buff_size());
start.set_node(map);
finish.set_node(map);
start.cur = start.first;
finish.cur = finish.first;
//空的状态是finish.cur == start.cur就表示为空了
}
//deque<int> a(20,9) deque<int> b(20)
deque(int n,const value_type& x = T()){
fill_initialize(n,x);
}
// deque<int> a = {1,2,3,4,5,6,7,8};
deque(std::initializer_list<T> l):deque(l.begin(),l.end()){}
//deque<int> a(b.begin(),b.end())
template<typename InputIterator>
deque(InputIterator first,InputIterator last):deque(){
//换一种写法,针对这堆数据,先定位到终点,一半push_front,一半push_back
//std::cout<<last - first<<std::endl;
/*difference_type mid = (last - first) / 2;
for(auto iter = first+mid;iter != first-1;--iter)
(*this).push_front(*iter);
for(auto iter = first+mid+1;iter != last;++iter)
(*this).push_back(*iter);
*/
//先创建map和对应的node
difference_type n = last - first;
create_map_and_nodes(n);
for(map_pointer cur = start.node;cur < finish.node;++cur){
//这样写会有问题,因为first+buff_size(),之后会变成下一个缓冲区的首个元素的地址
//内部copy调用memcpy会调用last - first时候出现问题
//TinySTL::uninitialized_copy(first,)
TinySTL::uninitialized_copy(first,first+buff_size(),*cur);
first = first + buff_size();
}
TinySTL::uninitialized_copy(first,last,finish.first);
}
//deque<int> a(b)
deque(deque<T>& x):deque(x.begin(),x.end()){}
//deque<int> a(std::move(b))
deque(deque<T>&& x){
//直接赋值
map = x.map;
map_size = x.map_size;
start = x.start;
finish = x.finish;
x.map = 0;
x.map_size = 0;
x.finish = x.start;
}
void push_back(const value_type& x){
if(finish.cur != finish.last -1){
//最后缓冲区尚有一个以上的备用空间
construct(finish.cur,x);
++finish.cur;
//std::cout<<"Process push_back_1"<<std::endl;
}else{
//如果最后缓冲区没有或者只剩下一个备用空间
push_back_aux(x);
//std::cout<<"Process push_back_2"<<std::endl;
}
}
void push_front(const value_type& x){
if(start.cur != start.first){
//还有空间,则构造
construct(start.cur-1,x);
--start.cur;
//std::cout<<"Process push_front_1"<<std::endl;
}else{
push_front_aux(x);
//std::cout<<"Process push_front_2"<<std::endl;
}
}
void pop_back(){
if(finish.cur != finish.first){
--finish.cur;
destroy(finish.cur);
}else{
//回收最后一个node
pop_back_aux();
}
}
void pop_front(){
if(start.cur != start.last-1){
destroy(start.cur);
++start.cur;
}else{
//回收第一个node
pop_front_aux();
}
}
void clear(){
//注意:deque最初的状态至少是一个缓冲区,因此clear析构掉所有元素
//但是仍然要保持有一个缓冲区
//首先将出去start和finish后中间的缓冲区都给析构并且回收掉
map_pointer cur = start.node;
//用!=可能会跳过finish,要用<号
for(++cur;cur < finish.node;++cur){
//把中间的缓冲区析构并且回收掉
destroy(*cur,*cur+buff_size());
data_allocator::deallocate(*cur,buff_size());
}
//然后判断首尾是否属于同一个
if(start.node == finish.node){
//如果首尾属于同一个,那就只析构元素,不回收缓冲区
//调整迭代器指向即可
destroy(start.cur,finish.cur);
}else{
//如果不是同一个,那就回收finish.node,保留start.node
destroy(finish.first,finish.cur);
data_allocator::deallocate(finish.first,buff_size());
destroy(start.cur,start.last);
}
//调整迭代器
finish = start;
}
//清除pos所指的元素,pos为清除点,返回清除后的迭代器
iterator erase(iterator pos){
iterator next = pos;
++next;
//计算清除点到起始点有多少个元素
difference_type index = pos - start;
if(index < size() / 2){
//说明清楚点前的元素更少,
//因此从前往后覆盖效率更高
//从前往后覆盖调用copy_backward()函数即可
std::copy_backward(start,pos,next);
pop_front();//然后把第一个元素出掉
}else{
//如果后面元素少,从后往前覆盖效率更高
//从后往前调用copy函数
std::copy(next,finish,pos);
pop_back();
}
return start+index;
}
//清除给定范围
iterator erase(iterator first,iterator last){
if(first == start && last == finish){
clear();
return finish;
}
difference_type n = last - first;//清除区间的长度
difference_type elems_before = first - start;//清除区间前面的元素个数
if(elems_before < (size() - n)/2){
//如果前方的元素量比较少,那就从前往后覆盖的效率更高
//从前往后覆盖调用copy_backward函数
//last是删除的最后元素的下一个位置,是不删除的
std::copy_backward(start,first,last);
//然后要对前面的地址进行析构回收了
iterator new_start = start + n;
destroy(start,new_start);
for(map_pointer cur = start.node;cur < new_start.node;++cur)
data_allocator::deallocate(*cur,buff_size());
start = new_start;
} else{
//如果后面的元素更少,则从后面往前面进行覆盖
std::copy(last,finish,first);
//然后把后面的析构掉,析构函数可以直接传递迭代器
//析构函数会根据是否是POD类型就型重载
iterator new_finish = finish - n;
destroy(new_finish,finish);
for(map_pointer cur = new_finish.node+1;cur <= finish.node;++cur)
data_allocator::deallocate(*cur,buff_size());
finish = new_finish;
}
//还是返回的删除后的iterator
return start+elems_before;
}
//insert要返回插入位置的iterator
iterator insert(iterator position,const value_type &x){
if(position.cur == start.cur){
//如果插入点是deque的最前端
push_front(x);
return start;
}else if(position.cur == finish.cur){
push_back(x);
return finish-1;
}else{
return insert_aux(position,x);
}
}
template<typename InputIterator>
iterator insert(iterator position,InputIterator first,InputIterator last){
if(position == start){
for(--last;first != last;--last){
push_front(*last);
}
push_front(*first);
return start;
}else if(position == finish){
//如果是尾部
for(;first != last;++first){
push_back(*first);
}
return finish-1;
}else{
return insert_aux(position,first,last);
}
}
//指定位置插入n个x
iterator insert(iterator position,const int& n,const value_type& x){
if(position == start){
for(int i=0;i<n;i++)
push_front(x);
return start;
}else if(position == finish){
//如果是尾部
for(int i=0;i<n;i++)
push_back(x);
return finish-1;
}else{
return insert_aux(position,size_type (n),x);
}
}
//接着可以根据迭代器来实现功能了
iterator begin(){return start;}
iterator end(){return finish;}
reference front(){return *start;}
reference back(){
return *(finish - 1);
}
reference operator[](const size_t& n){
//迭代器中已经实现了[]重载,可以直接调用了
return start[difference_type(n)];
}
size_type size(){
//return finish - start;
return size_type(finish-start);
}
//这个函数不知道是干嘛的
size_type max_size()const{return size_type(-1);}
bool empty()const{return finish == start;}
};
}
#endif //MYTINYSTLNEW_DEQUE_H