deque
底层数据结构
vector是单向开口的线性连续空间,deque则是一种双向开口的连续数据空间。所谓的双向开口,意思是可以在头尾两端分别做元素的插入和删除操作。当然vector也可以在头尾两端进行操作,但是其头部操作效果奇差,所以标准库没有为vector提供push_front或pop_front操作。与vector类似,deque支持元素的快速随机访问。deque的示意图如下:
现在问题来了:如果deque以数组来实现,如何做到在头部的常数时间插入?如果是采用链表来实现,又如何做到快速随机访问?deque的内部数据结构到底如何?想必你已经猜到了,要实现如上需求,需要由一段一段的连续空间链接起来的数据结构才能满足。
内存分配策略
接着上面讲。deque由一段一段的连续空间所链接而成,一旦需要在deque的前端或尾端增加新空间,便配置一段定量的连续空间,并将该空间串接在deque的头部或尾部。deque复杂的迭代器架构,构建出了所有分段连续空间”整体连续“的假象。
既然deque是由一段一段定长的连续空间所构成,就需要有结构来管理这些连续空间。deque采用一块map(非STL中的map)作为主控,map是一块小的连续空间,其中每个元素都是指针,指向一块较大的线性连续空间,称为缓冲区。而缓冲区才是存储deque元素的空间主体。示例图:
map本身也是一块固定大小的连续空间,当缓冲区数量增多,map容不下更多的指针时,deque会寻找一块新的空间来作为map。
deque的迭代器
为了使得这些分段的连续空间看起来像是一个整体,deque的迭代器必须有这样的能力:它必须能够指出分段连续空间在哪里,判断自己所指的位置s是否位于某一个缓冲区的边缘,如果位于边缘,则执行operator-- 或operator++时要能够自动跳到下一个缓冲区。因此,尽管deque的迭代器也是Ramdon Access Iterator 迭代器,但它的实现要比vector的复杂太多。SGI版本的STL deque实现思路可以看侯捷的《STL源码剖析》。
迭代器失效问题
(1)在deque容器首部或者尾部插入元素不会使得任何迭代器失效。
(2)在其首部或尾部删除元素则只会使指向被删除元素的迭代器失效。
(3)在deque容器的任何其他位置的插入和删除操作将使指向该容器元素的所有迭代器失效。
deque使用优劣
deque是在功能上合并了vector和list。
优点:(1) 随机访问方便,即支持[ ]操作符和vector.at()
(2) 在内部方便的进行插入和删除操作
(3) 可在两端进行push、pop
缺点:(1) 占用内存多
std::deque
template < class T, class Alloc = allocator<T> > class deque;
1、成员函数
构造 deque (公开成员函数)
从各种数据源构造新容器,可选地使用用户提供的分配器
1) 默认构造函数。构造空容器。
2) 构造拥有
count 个有值
value 的元素的容器。
4) 构造拥有范围
[first, last) 内容的容器。
5) 复制构造函数。构造拥有
other 内容的容器。若不提供
alloc ,则如同通过调用
std::allocator_traits<allocator_type>::select_on_container_copy_construction(other.get_allocator()) 获得分配器。
6) 移动构造函数。用移动语义构造拥有
other 内容的容器。分配器通过属于
other 的分配器移动构造获得。
7) 有分配器扩展的移动构造函数。将
alloc 用作新容器的分配器,从
other 移动内容;若
alloc != other.get_allocator() ,则它导致逐元素移动。
8) 构造拥有 initializer_list
init 内容的容器。
参数
复杂度
1) 常数
2-3) 与
count 成线性
4) 与
first 和
last 的距离成线性
5) 与
other 的大小成线性
6) 常数。
7) 若
alloc != other.get_allocator() 则为线性,否则为常数。
8) 与
init 的大小成线性。
异常 到 注意 在容器移动构造(重载 (6) )后,指向 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
析构 deque (公开成员函数) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
赋值给容器 (公开成员函数)
替换容器内容。
1) 复制赋值运算符。以
other 的副本替换内容。
若 std::allocator_traits<allocator_type>::propagate_on_container_copy_assignment::value 为 true ,则以源分配器的副本替换目标分配器。若源分配器与目标分配器不比较相等,则用目标( *this )分配器销毁内存,然后在复制元素前用 other 的分配器分配。 (C++11 起).、
2) 移动赋值运算符。用移动语义以
other 的内容替换内容(即从
other 移动
other 中的数据到此容器)。之后
other 在合法但未指定的状态。若
std::allocator_traits<allocator_type>::propagate_on_container_move_assignment::value 为
true ,则用源分配器的副本替换目标分配器。若它为
false 且源与目标分配器不比较相等,则目标不能取走源内存的所有权,而必须单独移动赋值逐个元素,用自己的分配器按需分配额外的内存。任何情况下,原先在
*this 中的元素要么被销毁,要么以逐元素移动赋值替换。
3) 以 initializer_list
ilist 所标识者替换内容。
参数
返回值*this 复杂度
1) 与
*this 和
other 的大小成线性。
2) 与
*this 的大小成线性,除非分配器不比较相等且不传播,该情况下与
*this 和
other 的大小成线性。
3) 与
*this 和
ilist 的大小成线性。
注意 容器移动赋值(重载 (2) )后,除非不兼容的分配器强制逐元素赋值,否则指向 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
将值赋给容器 (公开成员函数)
替换容器的内容。
1) 以
count 份
value 的副本替换内容。
2) 以范围
[first, last) 中元素的副本替换内容
3) 以来自 initializer_list
ilist 的元素替换内容。
所有指向容器元素的迭代器、指针及引用均被非法化。尾后迭代器亦被非法化。 参数
复杂度
1) 与
count 成线性
2) 与
first 和
last 间的距离成线性
3) 与
ilist.size() 成线性
| ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
返回相关的分配器 (公开成员函数)
返回与容器关联的分配器。 参数(无) 返回值关联的分配器。 复杂度常数。 |
#include<iostream>
#include<string>
#include<deque>
using namespace std;
//队列重载<<
template<typename T>
ostream& operator<<(ostream& s, const deque<T>& v)
{
s.put('[');
char comma[3] = { '\0', ' ', '\0' };
for (const auto& e : v) {
s << comma << e;
comma[0] = ',';
}
return s << ']';
}
int main()
{
deque<int>deq;
deq.push_back(1);
deq.push_back(2);
deq.push_back(3);
cout << "deq:" << deq << endl;
deque<int>deq1(deq.begin(), deq.end());
cout << "deq1:" << deq1 << endl;
//复制构造
deque<int>deq2(deq);
cout << "deq2:" << deq2 << endl;
deque<string>deq3(5, "AB");
cout << "deq3:" << deq3 << endl;
deque<int>num1{ 1, 2, 3, 4, 5, 6 };
deque<int>num2;
deque<int>num3;
cout << "num1:" << num1 << endl;
// 复制赋值会从 nums1 复制数据到 nums2
num2 = num1;
cout << "num2:" << num2 << endl;
// 移动赋值会从 nums1 移动数据到 nums3
num3 = move(num2);
cout << "num3:" << num3 << endl;
deque<char>ch;
ch.assign(6, '*');
cout << "ch:" << ch << endl;
//重新初始化对象
num1.assign(2, 6);
cout << "num1:" << num1 << endl;
deque<char>ch1;
ch1.assign(ch.begin(), ch.end());
cout << "ch1:" << ch1 << endl;
std::deque<int> mydeque;
int * p;
unsigned int i;
//分配数组5个元素的空间用于deque分配器
p = mydeque.get_allocator().allocate(5);
// 空间中赋值
for (i = 0; i<5; i++)
mydeque.get_allocator().construct(&p[i], i);
std::cout << "The allocated array contains:";
for (i = 0; i<5; i++) std::cout << ' ' << p[i];
std::cout << '\n';
// 销毁空间
for (i = 0; i<5; i++) mydeque.get_allocator().destroy(&p[i]);
mydeque.get_allocator().deallocate(p, 5);
system("pause");
return 0;
}
2、
元素访问 | |||||||||||||
访问指定的元素,同时进行越界检查 (公开成员函数)
返回位于指定位置 若 参数
返回值到所需元素的引用。 异常若 !(pos < size()) 则抛出 std::out_of_range | |||||||||||||
访问指定的元素 (公开成员函数)
返回位于指定位置 参数
返回值到所需元素的引用。 复杂度常数。 注意不同于 std::map::operator[] ,此运算符决不插入新元素到容器。 | |||||||||||||
访问第一个元素 (公开成员函数)
返回到容器首元素的引用。 在空容器上对 参数(无) 返回值到首元素的引用 复杂度常数 注意 对于容器 | |||||||||||||
访问最后一个元素 (公开成员函数)
返回到容器中最后一个元素的引用。 在空容器上对 参数(无) 返回值到最后元素的引用。 复杂度常数。 注意 对于容器 |
#include<iostream>
#include<string>
#include<deque>
using namespace std;
int main()
{
deque<string>str;
str.assign(2, "abac");
str.push_back("aaa");
str.push_back("bbb");
str.push_back("ccc");
cout << str.at(1) << endl;
cout << str[3] << endl;
cout << str.front() << endl;
cout << str.back() << endl;
system("pause");
return 0;
}
3、
迭代器 | |||||||||||||||||||
返回指向容器第一个元素的迭代器 (公开成员函数)
返回指向容器首元素的迭代器。 若容器为空,则返回的迭代将等于 end() 。 参数(无) 返回值指向首元素的迭代器 复杂度常数 | |||||||||||||||||||
返回指向容器尾端的迭代器 (公开成员函数)
返回指向后随容器最后元素的元素的迭代器。 此元素表现为占位符;试图访问它导致未定义行为。 参数(无) 返回值指向后随最后元素的迭代器。 复杂度常数。 | |||||||||||||||||||
返回一个指向容器最后一个元素的反向迭代器 (公开成员函数)
返回值指向逆向容器的首元素。它对应非逆向容器的最末元素。 参数(无) 返回值指向首元素的逆向迭代器。 复杂度常数 | |||||||||||||||||||
返回一个指向容器前端的反向迭代器 (公开成员函数)
返回逆向容器末元素的后一元素。它对应非逆向容器首元素的前一元素。此元素表现为占位符,试图访问它导致未定义行为。 参数(无) 返回值指向末元素后一元素的逆向迭代器。 复杂度常数。 |
#include<iostream>
#include<string>
#include<deque>
using namespace std;
int main()
{
deque<string>str;
str.assign(2, "abac");
str.push_back("aaa");
str.push_back("bbb");
str.push_back("ccc");
//迭代器
deque<string>::iterator iter = str.begin();
for (; iter != str.end();)
{
cout << *iter++ << ' ';
}
cout << endl;
//常迭代器
deque<string>::const_iterator iter1 = str.begin();
for (; iter1 != str.end();)
{
cout << *iter1++ << ' ';
}
cout << endl;
//常反迭代器
deque<string>::const_reverse_iterator iter2 = str.rbegin();
for (; iter2 != str.rend();)
{
cout << *iter2++ << ' ';
}
cout << endl;
system("pause");
return 0;
}
4、
容量 | |||||||||||||
检查容器是否为空 (公开成员函数)
检查容器是否无元素,即是否 begin() == end() 。 参数(无) 返回值若容器为空则为 true ,否则为 false 复杂度常数。 | |||||||||||||
返回容纳的元素数 (公开成员函数)
返回容器中的元素数,即 std::distance(begin(), end()) 。 参数(无) 返回值容器中的元素数量。 复杂度常数。 | |||||||||||||
返回可容纳的最大元素数 (公开成员函数)
返回根据系统或库实现限制的容器可保有的元素最大数量,即对于最大容器的 std::distance(begin(), end()) 。 参数(无) 返回值元素数量的最大值。 复杂度常数。 注意 此值典型地反映容器大小上的理论极限。运行时,容器的大小可能被可用 RAM 总量限制到小于 | |||||||||||||
(C++11)
| 通过释放未使用的内存减少内存的使用 (公开成员函数)
请求移除未使用的容量。 它是减少使用内存而不更改序列的大小非强制性请求。请求是否达成依赖于实现。 所有迭代器和引用都被非法化。尾后迭代器亦被非法化。 参数(无)
返回值(无) 复杂度至多与容器大小成线性。 注意若 T 移动构造函数以外的操作抛出异常,则无效果。 |
#include<iostream>
#include<string>
#include<deque>
#include<ctime>
using namespace std;
int main()
{
srand(time(0));
deque<char> ch;
for (int i = 0; i < 10; i++)
ch.push_back((char)rand() % 10);
cout << "ch size:" << ch.size() << endl;
if (ch.empty() == 0)
cout << "ch is null" << endl;
ch.resize(20);
//容器可保有的元素最大数量
cout << "ch max_size:" << ch.max_size() << endl;
// 调用 shrink_to_fit 可能会释放任何不使用的内存。
ch.shrink_to_fit();
cout << "ch size:" << ch.size() << endl;
system("pause");
return 0;
}
5、
修改器 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
清除内容 (公开成员函数)
从容器移除所有元素。 非法化任何指代所含元素的引用、指针或迭代器。任何尾后迭代器亦被非法化。 参数(无) 返回值(无) 复杂度与容器大小,即元素数成线性。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
插入元素 (公开成员函数)
插入元素到容器中的指定位置。
1-2) 在
pos 前插入
value 。
3) 在
pos 前插入
value 的
count 个副本。
4) 在
pos 前插入来自范围
[first, last) 的元素。
first 和
last 是指向
*this 中的迭代器,则行为未定义。
5) 在
pos 前插入来自 initializer_list
ilist 的元素。
所有迭代器,含尾后迭代器,都被非法化。引用亦被非法化,除非 pos == begin() 或 pos == end() ,该情况下它们不被非法化。 参数
返回值
1-2) 指向被插入
value 的迭代器。
3) 指向首个被插入元素的迭代器,或若
count==0 则为
pos 。
4) 指向首个被插入元素的迭代器,或若
first==last 则为
pos 。
5) 指向首个被插入元素的迭代器,或若
ilist 为空则为
pos 。
复杂度
1-2) 常数,加上与
pos 和容器两端距离的较小者成线性。
3) 与
count 成线性,加上与
pos 和容器两端距离的较小者成线性。
5) 与
ilist.size() 成线性,加上与
pos 和容器两端距离的较小者成线性。
异常若在任一端插入单个元素时抛出异常,则此函数无效果(强异常保证)。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(C++11)
| 原位构造元素 (公开成员函数)
直接于 所有迭代器,含尾后迭代器,都被非法化。引用亦被非法化,除非 pos == begin() 或 pos == end() ,该情况下它们不被非法化。 参数
返回值指向被安置的元素的迭代器。 复杂度 与 异常 若 value_type 的复制构造函数、移动构造函数、赋值运算符或移动赋值运算符以外的操作抛异常,或若在用 否则,效果未指定。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
擦除元素 (公开成员函数)
从容器移除指定的元素。
1) 移除位于
pos 的元素。
2) 移除范围
[first; last) 中的元素。
所有迭代器和引用都被非法化,除非被擦除的元素在容器的首或尾,该情况下仅指向被擦除元素的迭代器或引用被非法化。
迭代器 若 参数
返回值 后随最后被移除元素的迭代器。若迭代器 异常 不抛出,除非 复杂度线性:调用 T 析构函数的次数与被擦除的元素数相同,调用 T 赋值运算符的次数不多于被擦除元素前的元素数和被擦除元素后的元素数。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
将元素添加到容器末尾 (公开成员函数)
后附给定元素
1) 初始化新元素为
value 的副本。
2) 移动
value 进新元素。
所有迭代器,包含尾后迭代器,都被非法化。没有引用被非法化。 参数
返回值(无) 复杂度常数。 异常 若抛出异常(可能因为 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(C++11)
| 在容器末尾就地构造元素 (公开成员函数)
添加新元素到容器尾。元素通过 std::allocator_traits::construct 构造,它典型地用布置 new 于容器所提供的位置原位构造元素。参数 所有迭代器,包含尾后迭代器,都被非法化。没有引用被非法化。 参数
返回值
复杂度常数。 异常若抛出异常,则此函数无效果(强异常保证)。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
移除末元素 (公开成员函数)
移除容器的最末元素。 在空容器上调用
参数(无) 返回值(无) 复杂度常数。 异常(无) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
插入元素到容器起始 (公开成员函数)
前附给定元素 所有迭代器,包含尾后迭代器,都被非法化。没有引用被非法化。 参数
返回值(无) 复杂度常数。 异常若抛出异常,则此函数无效果(强异常保证)。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
(C++11)
| 在容器头部就地构造元素 (公开成员函数)
插入新元素到容器起始。通过 std::allocator_traits::construct 构造元素,它典型地用布置 new 在容器所提供的位置原位构造元素。将参数 所有迭代器,包含尾后迭代器,都被非法化。没有引用被非法化。 参数
返回值
复杂度常数。 异常若抛异常,则此函数无效果(强异常保证)。 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
移除首元素 (公开成员函数)
移除容器首元素。若容器中无元素,则行为未定义。
参数(无) 返回值(无) 复杂度常数。 异常不抛出。 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
改变容器中可存储元素的个数 (公开成员函数)
重设容器大小以容纳 若当前大小大于
参数
返回值(无) 复杂度 与当前大小和 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
交换内容 (公开成员函数)
将内容与 所有迭代器和引用保持合法。尾后迭代器被非法化。
参数
返回值(无) 异常
复杂度常数。 |
#include<iostream>
#include<string>
#include<deque>
#include<ctime>
#include<vector>
using namespace std;
int main()
{
deque<string> str;
str.push_back("Hello");
str.push_back("World");
cout << "str: " << str.at(0) << " size:" << str.size() << endl;
str.clear();
if (!str.empty())
cout << "str: " << str.at(0) << endl;
else
cout<< "size:" << str.size() << endl;
deque<int> mydeque;
//插入
// set some initial values:
for (int i = 1; i<6; i++)
mydeque.push_back(i); // 1 2 3 4 5
deque<int>::iterator it = mydeque.begin();
++it;
it = mydeque.insert(it, 10); // 1 10 2 3 4 5
// "it" now points to the newly inserted 10
mydeque.insert(it, 2, 20); // 1 20 20 10 2 3 4 5
// "it" no longer valid!
it = mydeque.begin() + 2;
vector<int> myvector(2, 30);
mydeque.insert(it, myvector.begin(), myvector.end());
// 1 20 30 30 20 10 2 3 4 5
cout << "mydeque contains:";
for (it = mydeque.begin(); it != mydeque.end(); ++it)
cout << ' ' << *it;
cout << '\n';
deque<int> mydeque1 = { 10, 20, 30 };
auto it1 = mydeque1.emplace(mydeque1.begin() + 1, 100);
mydeque1.emplace(it1, 200);
mydeque1.emplace(mydeque1.end(), 300);
cout << "mydeque contains:";
for (auto& x : mydeque1)
cout << ' ' << x;
cout << '\n';
mydeque1.emplace_back(100);
mydeque1.emplace_back(200);
cout << "mydeque contains:";
for (auto& x : mydeque1)
cout << ' ' << x;
cout << '\n';
deque<string> str1;
str1.push_back("Hello");
str1.push_back("world");
cout << "str1: " << str1.at(0) << " size:" << str1.size() << endl;
str1.erase(str1.begin());
if (!str1.empty())
cout << "str1: " << str1.at(0) << " size:" << str1.size() << endl;
else
cout << "size:" << str1.size() << endl;
deque<int> arr{ 1, 2, 3, 4, 5, 6 };
for (deque<int>::iterator it = arr.begin(); it != arr.end(); it++)
cout << *it << ' ';
cout << endl;
//移除末元素
arr.pop_back();
for (deque<int>::iterator it = arr.begin(); it != arr.end(); it++)
cout << *it << ' ';
cout << endl;
//移除首元素
arr.pop_front();
for (deque<int>::iterator it = arr.begin(); it != arr.end(); it++)
cout << *it << ' ';
cout << endl;
//首元素插入
arr.push_front(6);
arr.push_front(6);
arr.push_front(6);
for (deque<int>::iterator it = arr.begin(); it != arr.end(); it++)
cout << *it << ' ';
cout << endl;
deque<int>arr1(5, 9);
cout << "arr1 size:" << arr1.size() << endl;
arr1.swap(arr);
cout << "arr1 size:" << arr1.size() << endl;
for (deque<int>::iterator it = arr.begin(); it != arr.end(); it++)
cout << *it << ' ';
cout << endl;
system("pause");
return 0;
}
6、
非成员函数
按照字典顺序比较 deque 中的值 (函数模板)
比较二个容器的内容。
1-2) 检查
lhs 与
rhs 的内容是否相等,即是否
lhs.size() == rhs.size() 且每个
lhs 中的元素与
rhs 的同位置元素比较相等。
3-6) 按字典序比较
lhs 与
rhs 的内容。按照等价于
std::lexicographical_compare 的函数进行比较。
参数
返回值
1) 若容器内容相等则为
true ,否则为
false
2) 若容器内容不相等则为
true ,否则为
false
3) 若
lhs 的内容按字典序
小于
rhs 的内容则为
true ,否则为
false
4) 若
lhs 的内容按字典序
小于或
等于
rhs 的内容则为
true ,否则为
false
5) 若
lhs 的内容按字典序
大于
rhs 的内容则为
true ,否则为
false
6) 若
lhs 的内容按字典序
大于或
等于
rhs 的内容则为
true ,否则为
false
复杂度与容器大小成线性 | |||||||||||||||||||||||||||||||
特化 std::swap 算法 (函数模板)
为 std::deque 特化 std::swap 算法。交换 参数
返回值(无) 复杂度常数。 异常
|
// deque comparisons
#include <iostream>
#include <deque>
int main()
{
std::deque<int> foo(3, 100); // three ints with a value of 100
std::deque<int> bar(2, 200); // two ints with a value of 200
if (foo == bar) std::cout << "foo and bar are equal\n";
if (foo != bar) std::cout << "foo and bar are not equal\n";
if (foo< bar) std::cout << "foo is less than bar\n";
if (foo> bar) std::cout << "foo is greater than bar\n";
if (foo <= bar) std::cout << "foo is less than or equal to bar\n";
if (foo >= bar) std::cout << "foo is greater than or equal to bar\n";
system("pause");
return 0;
}
// swap (deque overload)
#include <iostream>
#include <deque>
int main()
{
unsigned int i;
std::deque<int> foo(3, 100); // three ints with a value of 100
std::deque<int> bar(5, 200); // five ints with a value of 200
swap(foo, bar);
std::cout << "foo contains:";
for (std::deque<int>::iterator it = foo.begin(); it != foo.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
std::cout << "bar contains:";
for (std::deque<int>::iterator it = bar.begin(); it != bar.end(); ++it)
std::cout << ' ' << *it;
std::cout << '\n';
system("pause");
return 0;
}
待续。。。。。。