STL学习——STL中的序列式容器及适配器总结(vector、list、deque、stack、queue)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a987073381/article/details/52124064
所谓序列式容器、其中的元素都可序,但未必有序。STL提供了vector,list,deque,stack,queue,priority-queue等等序列式容器。其中stack和queue由于只是将deque改头换面,技术上被归类为一种配接器(adapter),以下总结了序列式容器。

一、vector—会自动增长的数组
(1)功能
vector采用的是 动态数组。vector将其元复制到内部的动态数组中。元素之间总是存在某种顺序,它是一种有序群集。支持随即存取。
vector是动态空间,随着元素的加入,它的内部机制会自行扩容以容纳新元素。向vector添加一个元素或者删除其中的一个元素,其后的所有元素都要移动位置,每一次移动都要对其后的所有元素调用赋值操作符。vector的实现技术,关键在于对大小的控制以及重新配置时的数据移动效率。STL实现者在对vector进行内存分配时,其实际分配的容量要比当前所需的空间多一些。就是说,vector容器预留了一些额外的存储区,用于存放新添加的元素,这样就不必为每个新元素重新分配整个容器的内存空间。

容器的capacity(容量)与size(长度)的区别:
size:指容器当前拥有的元素个数;
capacity:则指容器在必须分配新存储空间之前可以存储的元素总数,也可以说是预分配存储空间的大小。

(2)元素操作
元素存取:
value_type operator[](size_type index);//返回下标为index的元素
//vct.at(index);//返回下标为index的元素
value_type front();//返回第一个元素(不检查是否存在)
value_type back();//返回最后一个元素(不检查是否存在)
迭代器相关:
value_type operator[](size_type index);//返回下标为index的元素
//vct.at(index);//返回下标为index的元素
value_type front();//返回第一个元素(不检查是否存在)
value_type back();//返回最后一个元素(不检查是否存在)
插入和移除元素:
iterator insert(iterator position, const T& elem);//在指定位置插入新元素,返回新元素的指针
void insert (iterator pos, size_type n, const T& elem);//在指定位置插入n个elem
void insert(iterator position, InputIterator first, InputIterator last);//在指定位置插入从first到last的元素
void push_back(const T& elem);//在数组最后面添加一个元素
void pop_back();//移走最后一个元素
iterator erase(iterator position);//移走指定位置的元素,返回下一个元素的指针
iterator erase(iterator first, iterator last);//移走所有[first, last]直接的元素,返回下一个元素的指针
void resize(size_type new_size);//容器的size置为new_size
void resize(size_type new_size, const T& elem);//容器的size置为new_size,新的元素都为elem
void clear();//移除vector所有元素

(3)迭代器
vector维护的是一个连续线性空间,所以无论其元素类型如何,普通指针都可以作为vector的迭代器而满足所有必要条件,普通指针可以支持vector 随机存取。迭代器失效的两种情况是:1、在一个较小的位置上删除或者是移动元素;2、由于容量的变换引起内存重新分配。

(4)内存管理
vector缺省使用alloc作为空间配置器,另外还定义了一个data_allocator,为的是更方便以大小为配置单位。
vector的容量十分重要,是因为: 1、一旦内存重新配置,与之相关的所有的reference、pointers、iterators都会失效; 2、内存配置很费时。
解决这个问题的方法: reserve()函数和容器的capacity相关,调用reserve(n)后,若容器的capacity<n,则重新分配内存空间,从而使得capacity等于n,如果capacity>=n则无变化。
注意:resize()函数和容器的size相关,调用resize(n)后,容器的size即为n。

二、list—擅长插入删除的链表
(1)功能
list采用一个 双向链表来管理元素,它的好处是每次插入或删除一个元素,就配置或释放一个元素空间,而且时间复杂度都是常数级。list不支持随机存取。所及既不提供下表操作符,也不提供at()函数。

(2)元素操作
元素存取:
value_type front();//返回第一个元素(不检查是否存在)
value_type back();//返回最后一个元素(不检查是否存在)
注意:这里少了vector的at和[]运算符的随机访问数据的方法。
迭代器相关:
iterator begin();//返回第一个元素的指针
iterator end();//返回最后一个元素的指针
插入和移除元素:
iterator insert(iterator position, const T& elem);//在指定位置插入新元素,返回新元素的指针
void insert (iterator pos, size_type n, const T& elem);//在指定位置插入n个elem
void insert(iterator position, InputIterator first, InputIterator last);//在指定位置插入从first到last的元素
void push_back(const T& elem);//在数组最后面添加一个元素
void pop_back();//移走最后一个元素
void push_front(const T& elem);//在数组最前面添加一个元素elem
void pop_front();//移走数组最前面的元素elem
iterator erase(iterator position);//移走指定位置的元素,返回下一个元素的指针
iterator erase(iterator first, iterator last);//移走所有[first, last]直接的元素,返回下一个元素的指针
void resize(size_type new_size);//容器的size置为new_size
void resize(size_type new_size, const T& elem);//容器的size置为new_size,新的元素都为elem
void clear();//移除vector所有元素

(3)迭代器
插入和删除操作 不会造成指向其他元素的pointers、references、iterators失效。存由于list是一个双向链表,迭代器必须具备前移、后移的能力,所以list提供的是bidirectional iterators,取元素只能用迭代器。然而由于list 不能 随机存取,这些迭代器 只是双向而非随机迭代器。所以凡是用到随机存取迭代器的算法(所有用来操作元素顺序的算法-特别是排序算法都归于此类)都不能调用。不过可以用list的成员函数 sort()完成此功能。

(4)内存管理
list缺省使用alloc作为空间配置器,另外还定义了一个list_node_allocator,为的是更方便以节点大小为配置单位。
list不像vector那样有可能在空间不足时重新配置、数据移动操作,所以插入前的所有迭代器在插入操作后都有效。

三、deque—综合vector和list两者优点的双端队列
(1)功能
deque是一种 双向开口的连续线性空间,也就是可以在头尾两端分别做元素的插入和删除操作。 deque是stack和queue的默认容器。
deque和vector的最大差异,一在于deque允许在常数时间内对头端进行元素的插入或移除操作,二在于deque没有所谓容量的概念,因为它是 动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。所以不会像vector一样——因为旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间。因此,deque没必要提供空间保留(reserve)功能。

(2)元素操作
deque的各项操作可以参考vector,但是注意有以下几个不同:
deque不提供容量操作:capacity()和reverse()。
deque直接提供函数完成首尾元素的插入和删除。

(3)中控器
deque采用一块map(不是map容器)作为主控,每个map是一小块连续空间,其中每一个元素都是指针,指向另一段连续线性空间,缓冲区才是deque真正存放数据的地方。

(4)迭代器
deque也提供ramdon access iterator,但是它的迭代器不是普通指针,如果迭代器一直在某一个缓冲区中,我们可以把当做普通指针,但是一旦行进到缓冲区边缘,视前进或后退而定,可能需要调用set_not()跳一个缓冲区,所以我们经常遍历容器中的元素时应尽可能使用vector而非deque。对deque进行排序操作,最好先复制一个vector上,排序后再复制回deque。

vector、list和deque的终极总结:
(1)vector适用于随机存取,支持[],随机存取时间为常数;但是在非末尾删除插入数据时,效率极低。
(2)list适用于数组中间频繁插入删除数据,其插入删除开销低,但是不支持随机存取。
(3)deque界于vector和list两者之间,支持随机存取,但是性能没有vector好。随机存取效率接近于vector(开销大),队首队尾插入删除接近于list(开销小)。最好采用deque的情形:1、需要在两端插入和删除元素。2、无需引用容器内的元素。3、要求容器释放不再使用的元素。

四、stack
(1)功能
stack是一种先进后出的数据结构,stack只允许新增元素、移除元素、取得最顶端元素,但是除了最顶端外,没有其他办法可以存取stack的其它元素,也就是说 stack不允许有遍历行为

(2)元素操作
......
reference top();//返回栈中最上面的元素
void push(const value_type& elem);//将一个元素压栈
void pop();//移走栈顶的一个元素

(3)迭代器
stack所有元素的进出必须符合”先进后出“的条件,只有stack顶端的元素才有机会被外界取用,所以stack不提供走访功能,也不提供迭代器。

五、queue
(1)功能
queue是一种先进先出的数据结构,它有两个出口,queue只允许新增元素、移除元素、从最底端假如元素,取得最顶端元素,但是除了最底端可以加入、最顶端可以取出外,没有其他办法可以存取queue的其它元素,也就是说 queue也允许有遍历行为

(2)元素操作
......
reference front();//返回队头元素
reference back();//返回队尾元素
void push(const value_type& elem);//将一个元素假如队列
void pop();//移走队列的第一个元素

(3)迭代器
queue所有元素的进出必须符合”先进先出“的条件,只有queue顶端的元素才有机会被外界取用,所以queue不提供走访功能,也不提供迭代器。
STL中迭代器失效处理——erase的使用
序列性容器::(vector和list和deque)
erase迭代器不仅使所有指向被删元素的迭代器失效,而且使被删元素之后的所有迭代器失效,所以不能使用erase(iter++)的方式,但是erase的返回值为下一个有效的迭代器,所以正确方法为::
for( iter = c.begin(); iter != c.end(); )
                iter = c.erase(iter);


参考:
《STL源码剖析》

猜你喜欢

转载自blog.csdn.net/a987073381/article/details/52124064