序列式容器
默认数据无序排列,但可排序操作
STL提供三个序列式容器
- vector
- deque
- list
*可将strings和array当作序列式容器,但strings和array不属于STL容器。
vector
-
参考
vector是表示可以动态改变大小的数组的序列容器。
构造函数
default (1) | explicit vector (const allocator_type& alloc = allocator_type()); |
---|---|
fill (2) | explicit vector (size_type n); vector (size_type n, const value_type& val, const allocator_type& alloc = allocator_type()); |
range (3) | template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type()); |
copy (4) | vector (const vector& x); vector (const vector& x, const allocator_type& alloc); |
move (5) | vector (vector&& x); vector (vector&& x, const allocator_type& alloc); |
initializer list (6) | vector (initializer_list<value_type> il, const allocator_type& alloc = allocator_type()); |
// 默认allocator为alloc, 其具体使用版本请参照<stl_alloc.h>
template <class T, class Alloc = alloc>
class vector
{
public:
// 标记为'STL标准强制要求'的typedefs用于提供iterator_traits<I>支持
typedef T value_type; // STL标准强制要求
typedef value_type* pointer; // STL标准强制要求
typedef const value_type* const_pointer;
// 由于vector的特性, 一般我们实作的时候都分配给其连续的内存空间,
// 所以其迭代器只需要定义成原生指针即可满足需要
typedef value_type* iterator; // STL标准强制要求
typedef const value_type* const_iterator;
typedef value_type& reference; // STL标准强制要求
typedef const value_type& const_reference;
typedef size_t size_type;
typedef ptrdiff_t difference_type; // STL标准强制要求
protected:
// 这个提供STL标准的allocator接口
typedef simple_alloc<value_type, Alloc> data_allocator;
iterator start; // 内存空间起始点
iterator finish; // 当前使用的内存空间结束点
iterator end_of_storage; // 实际分配内存空间的结束点
}
// 获取几种迭代器
iterator begin() { return start; }
const_iterator begin() const { return start; }
iterator end() { return finish; }
const_iterator end() const { return finish; }
reverse_iterator rbegin() { return reverse_iterator(end()); }
const_reverse_iterator rbegin() const {
return const_reverse_iterator(end());
}
reverse_iterator rend() { return reverse_iterator(begin()); }
const_reverse_iterator rend() const {
return const_reverse_iterator(begin());
}
// 返回当前对象个数
size_type size() const { return size_type(end() - begin()); }
size_type max_size() const { return size_type(-1) / sizeof(T); }
// 返回重新分配内存前最多能存储的对象个数
size_type capacity() const { return size_type(end_of_storage - begin()); }
bool empty() const { return begin() == end(); }
reference operator[](size_type n) { return *(begin() + n); }
const_reference operator[](size_type n) const { return *(begin() + n); }
构造函数示例
// constructing vectors
#include <iostream>
#include <vector>
#include <assert.h>
#include <iterator> //istream_iterator,ostream_iterator,back_inserter
//using namespace std;
int main ()
{
// 0. Create an empty vector v0
std::vector<int> v0;
assert(v0.empty());
// 1. Create a vector v1 with 3 elements of default value 0
std::vector<int> v1(3);
// 2. Create a vector v2 with 5 elements of value 2
std::vector<int> v2(5, 2);
// 3. Create a vector v3 with 3 elements of value 1 and with the allocator of vector v2
std::vector<int> v3(3, 1, v2.get_allocator());
// 4. Create a copy, vector v4, of vector v2
std::vector<int> v4(v2);
// 5. Create a vector v5 by copying the range v4[_First, _Last)
std::vector<int> v5(v4.begin() + 1, v4.begin() + 3);
std::cout << "v1 = ";
std::copy(v1.begin(), v1.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::cout << "v2 = ";
std::copy(v2.begin(), v2.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::cout << "v3 = ";
std::copy(v3.begin(), v3.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::cout << "v4 = ";
std::copy(v4.begin(), v4.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
std::cout << "v5 = ";
std::copy(v5.begin(), v5.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
// 6. Move vector v2 to vector v6
std::vector<int> v6(std::move(v2));
std::cout << "v6 = ";
std::copy(v6.begin(), v6.end(), std::ostream_iterator<int>(std::cout, " "));
std::cout << std::endl;
}
// constructing vectors
#include <iostream>
#include <vector>
int main ()
{
// constructors used in the same order as described above:
std::vector<int> first; // empty vector of ints
std::vector<int> second (4,100); // four ints with value 100
std::vector<int> third (second.begin(),second.end()); // iterating through second
std::vector<int> fourth (third); // a copy of third
// the iterator constructor can also be used to construct from arrays:
int myints[] = {16,2,77,29};
std::vector<int> fifth (myints, myints + sizeof(myints) / sizeof(int) );
myints[2] = 0;
std::cout << "The contents of fifth are:";
for (std::vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
return 0;
}
//输出:The contents of fifth are: 16 2 77 29
成员函数
-
注意
vector<char> v;
v.resize(41);
strcpy(&v[0],"hello world!");
printf("%s\n",&v[0]); //OK
printf("%s\n",v.begin()); //ERROR
迭代器不应该作为第一个元素的地址传递。因为迭代器不一定直接由指针实现(此版本的vector的迭代器刚好直接由指针实现)
template <class T, class Alloc = alloc>
class vector
{
typedef T value_type;
typedef value_type* iterator;
}
deque
-
参考
-
特点
deque是一种优化了的、对序列两端元素进行添加和删除操作的基本序列容器。它允许较为快速地随机访问,但它不像vector 把所有的对象保存在一块连续的内存块,而是采用多个连续的存储块,并且在一个映射结构中保存对这些块及其顺序的跟踪。向deque 两端添加或删除元素的开销很小。它不需要重新分配空间,所以向末端增加元素比vector 更有效。
实际上,deque 是对vector 和list 优缺点的结合,它是处于两者之间的一种容器。
(1) 随机访问方便,即支持[ ] 操作符和vector.at() ,但性能没有vector 好;
(2) 可以在内部进行插入和删除操作,但性能不及list ;
(3) 可以在两端进行push 、pop ;
(4) 相对于verctor 占用更多的内存。
双向队列和向量很相似,但是它允许在容器头部快速插入和删除(就像在尾部一样)。
可以看出deque头部的插入和删除操作与尾部性能一致 ,但在中间的插入和删除操作性能不及list 。
deque和vector的不同之处
1、两端都能够快速插入和删除元素。vector只能在尾端进行。
2、deque的元素存取和迭代器操作会稍微慢一些。因为deque的内部结构会多一个间接过程。
3、迭代器是特殊的智能指针,而不是一般指针。它需要在不同的区块之间跳转。
4、deque可以包含更多的元素,其max_size可能更大。因为不止使用一块内存。
5、不支持对容量和内存重分配时机的控制。在除了首尾两端的其他地方插入和删除元素,都将会导致指向deque元素的任何pointers、references、iterators失效。不过,deque的内存重分配优于vector。因为其内部结构显示不需要复制所有元素。
6、deque的内存区块不再被使用时,会被释放。deque的内存大小是可缩减的。不过,是不是这么做以及怎么做由实作版本定义。
deque和vector相似的特性:
1、在中间部分插入和删除元素相对较慢,因为所有元素都要被移动。
2、迭代器属于随即存取迭代器。
最好采用deque的情形:
1、需要在两端插入和删除元素。
2、无需引用容器内的元素。
3、要求容器释放不再使用的元素。
注意:
1、除了at()函数,其他成员函数都不会检查索引或迭代器是否有效。
2、元素的插入和删除可能会导致内存重新分配。所以任何插入或删除操作都会使所有指向deque元素的pointers、reference、iterators失效。唯一例外的是在首尾插入元素之后,pointers和reference可能仍然有效,但是iterators失效。
#include <iostream>
#include <deque>
#include <string>
#include <algorithm>
#include <iterator>
using namespace std;
int main()
{
// create empty deque of strings
deque<string> coll;
// insert several elements
coll.assign (3, string("string")); //将3个"string"副本赋值给coll
coll.push_back ("last string");
coll.push_front ("first string");
// print elements separated by newlines
copy (coll.begin(), coll.end(),
ostream_iterator<string>(cout,"\n"));
cout << endl;
// remove first and last element
coll.pop_front();
coll.pop_back();
// insert ``another'' into every element but the first
for (unsigned i=1; i<coll.size(); ++i) {
coll[i] = "another " + coll[i];
}
// change size to four elements
coll.resize (4, "resized string"); //将大小(元素的数目)改为4,如果大小增长,增加的元素都是"resized string"的副本
// print elements separated by newlines
copy (coll.begin(), coll.end(),
ostream_iterator<string>(cout,"\n"));
}
list
list使用双向链表管理元素。
-
特点
list的内部结构和vector或deque截然不同,所以在几个主要方面与vector和deque存在明显区别:
1.list不支持随机存取,即如果你要存取第5个元素,你必须顺着串链一一爬过前4个元素,所以在list中随机遍历任意元素,是很缓慢的行为;
2.任何位置(不只是两端)执行元素的安插和删除都非常快,始终是常数时间内完成,因为无需移动任何其他元素,实际上内部只是进行了一些指针操作而已;
3.安插和删除动作并不会造成指向其他元素的各个pointers,references,iterators失效;
4.list对于异常有着:要么操作成功,要么什么都不发生。
5.list提供了不少特殊的成员函数,专门用于移动元素,较通用的STL算法,这些函数执行起来速度更快,因为无须拷贝和移动,只需调整若干指针即可;
6.list内部提供了很多高效快速的成员函数,用来移动list内部元素或者不同list之间的元素;
list所提供的成员函数反应出它和vector以及deque的不同:
1. 由于不支持随机存取,list既不提供下标操作符,也不提供at()
2. list并未提供容量、空间重新分配等操作函数,因为没有必要。每个元素都有自己的内存。
3. list提供不少特殊的成员函数,专门用于移动元素。
-
函数
#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>
using namespace std;
void printLists (const list<int>& l1, const list<int>& l2)
{
cout << "list1: ";
copy (l1.begin(), l1.end(), ostream_iterator<int>(cout," "));
cout << endl << "list2: ";
copy (l2.begin(), l2.end(), ostream_iterator<int>(cout," "));
cout << endl << endl;
}
int main()
{
// create two empty lists
list<int> list1, list2;
// fill both lists with elements
for (int i=0; i<6; ++i) {
list1.push_back(i);
list2.push_front(i);
}
printLists(list1, list2);
// insert all elements of list1 before the first element with value 3 of list2
// - find() returns an iterator to the first element with value 3
list2.splice(find(list2.begin(),list2.end(), // destination position
3),
list1); // source list
printLists(list1, list2);
// move first element to the end
list2.splice(list2.end(), // destination position
list2, // source list
list2.begin()); // source position
printLists(list1, list2);
// sort second list, assign to list1 and remove duplicates
list2.sort();
list1 = list2;
list2.unique();
printLists(list1, list2);
// merge both sorted lists into the first list
list1.merge(list2);
printLists(list1, list2);
}