Deque
Deque与Vector非常相似,它也采用dynamic array进行管理,提供随机访问,同时具有与vector一模一样的接口。不同的是Deque的dynamic array头尾都是开放的,因此可以在两端进行快速的插入和删除。
为了提供这种能力,deque通常实现了一种独立区块(a bunch of individual blocks),第一区块朝某一方向扩展,最末区块朝另一方向扩展。
使用Deque之前,必须包含头文件<deque>
。
#include<deque>
其中,deque的类型在std命名空间std内一个class template为:
template<
class T,
class Allocator = std::allocator<T>
> class deque;
和vector相同,这里的T是为了template表明元素类型的,而Allocator则是指定内存模型(memory model)。
Deque的能力
Deque与Vector相比,有所相同,又有所不同,下面是两者的异同点:
相同之处
1.支持随机访问,迭代器均属于random-access iterator;
2.基于中间位置的元素的移除和插入,速度都比较慢,因为要进行大量元素的移动和复制操作;
3.vector所支持的接口在deque上都能使用,且具有相同的效果。
不同之处
1.两端都能够进行快速的插入和移除操作;
2.访问deque时,内部结构会多一个间接过程,因此元素的访问以及迭代器的动作会相比vector较慢;
3.迭代器需要在不同的区块间进行跳转,因此迭代器必须是smart_pointer,不能是寻常pointer;
4.Deque不支持对容量大小的控制,需要特别注意的是,除了首尾两端,在任何地点安插或者删除元素都会导致pointer、reference和iterator的失效;
5.Deque重新分配内存优于vector,因为其内部结构显示,deque重新分配内存的时候,不需要复制所有的元素;
6.Deque会释放不需要的内存块,Deque的大小是可缩减的,但是要不要这么做,如何做,取决于编译器。
总结:显然,deque具有vector的特性,且比vector更强大,但C++之中,更强大的功能往往意味这更大的时空开销,如何在功能和开销上作取舍,取决于具体应用场景。
Deque适用场景
1.移除和插入操作发生在首尾两端(Deque的特性决定了该操作效率惊人);
2.无须迭代器指向其元素(Deque扩容机制导致了其迭代器更容易失效);
3.要求不再使用的元素必须释放(Deque能够释放不使用的内存块,但C++ standard并不保证这一点,依赖于编译器实现)。
Deque相关操作
构造、复制和销毁
Deque的构造与析构函数,设计逻辑与Vector几乎相同。
序号 | 操作 | 效果 |
---|---|---|
1 | deque<Elem> c | Deque默认构造函数,生成一个deque,没有任何元素 |
2 | deque<Elem> c(c2) deque<Elem> c=c2 |
Copy构造函数,支持深度拷贝 |
3 | deque<Elem> c(rv) deque<Elem> c=rv |
Move构造函数,rv是右值引用(C++11新特性) |
4 | deque<Elem> c(n) | 生成一个大小为n的deque |
5 | deque<Elem> c(n,elem) | 生成一个大的小为n的deque,并初始化元素为elem |
6 | deque<Elem> c(beg,end) | 生成一个deque,以区间[beg,end)作为元素初值 |
7 | deque<Elem> c(initlist) | 生成一个deque,并以初值列进行初始化 |
8 | c.~deque() | 销毁所有元素,释放内存 |
非更易型操作(Nonmodifying Operating)
与Vector相同,Deque的非更易型操作如下表所示:
序号 | 操作 | 效果 |
---|---|---|
1 | c.empty() | 返回容器是否为空,为空则返回true,否则false |
2 | c.size() | 返回目前元素的个数 |
3 | c.max_size() | 返回元素个数的最大可能量,即自动分配内存的极限 |
4 | c.shrink_to_fit() | 降低容量以符合元素个数(C++11新特性) |
5 | c1==c2 c1!=c2 c1>c2 c1<c2 c1<=c2 c1>=c2 |
容器的比较操作都是基于每个元素的,即所有的元素都满足比较关系才返回true,否则是false |
6 | c[index] | 返回索引index所指向的元素(不检查边界) |
7 | c.at(index) | 返回索引index所指向的元素(检查边界,越界会抛出range-error异常) |
8 | c.front() | 返回第一元素 |
9 | c.back() | 返回最末元素 |
10 | c.begin() | 返回一个random-access iterator,指向第一个元素 |
11 | c.end() | 返回一个random-access iterator,指向最后一个元素 |
12 | c.cbegin() | 返回一个const random-access iterator,指向第一个元素(C++11新特性) |
13 | c.cend() | 返回一个const random-access iterator,指向最后一个元素(C++11新特性) |
14 | c.rbegin() | 返回一个reverse iterator,指向第一个元素 |
15 | c.rend() | 返回一个reverse iterator,指向最后一个元素 |
16 | c.crbegin() | 返回一个const reverse iterator,指向第一个元素(C++11新特性) |
17 | c.crend() | 返回一个const reverse iterator,指向最后一个元素(C++11新特性) |
举个例子——
deque<int> c{ 1,2,3,4,5,6,7,8,9 };
cout << "c.empty() = " << c.empty() << endl
<< "c.size() = " << c.size() << endl
<< "c.max_size()" << c.max_size() << endl;
for (auto it = c.cbegin(); it != c.cend(); it++) {
cout << *it << " ";
}
更易型操作
Deque的更易型操作,如下表所示:
序号 | 操作 | 效果 |
---|---|---|
1 | c = c2 | 将c2的数据复制给c,该复制是深度复制 |
2 | c = rv | 将rvalue rv的所有元素以move assignment的形式给c(C++11新特性) |
3 | c = initlist | 将初值列所有元素赋值给c(C++11新特性) |
4 | c.assign(n,elem) | 复制n个elem元素给c |
5 | c.assign(beg,end) | 复制区间[beg,end)元素给c |
6 | c.assign(initlist) | 复制初值列所有元素给c |
7 | c1.swap(c2) swap(c1,c2) |
置换c1和c2的数据 |
8 | c.push_back(elem) | 附加一个elem元素到deque末尾 |
9 | c.pop_back() | 移除末尾的一个元素,不返回任何值 |
10 | c.push_front() | 附加一个elem元素到首部 |
11 | c.pop_front() | 移除首部的一个元素,不返回任何值 |
12 | c.insert(pos,elem) | 在iterator指向的pos位置的前一个位置插入elem拷贝,并返回新元素的位置 |
13 | c.insert(pos,n,elem) | 在iterator指向的pos位置的前一个位置插入n个elem拷贝,并返回第一个新元素的位置 |
14 | c.insert(pos,beg,end) | 在iterator指向的pos位置的前一个位置插入区间[beg,end)所有元素拷贝,并返回第一个新元素的位置 |
15 | c.insert(pos,initlist) | 在iterator指向的pos位置的前一个位置插入initlist初值列拷贝,并返回第一个新元素的位置(C++11新特性) |
16 | c.emplace(pos,args…) | 在iterator位置pos之前插入一个以args为初值的运输,并返回新元素的位置(C++11新特性) |
17 | c.emplace_back(args…) | 插入一个args初值于末尾,不返回任何东西(C++11新特性) |
18 | c.emplace_front(args…) | 插入一个args初值于首部,不返回任何东西(C++11新特性) |
19 | c.erase(pos) | 移除iterator位置pos的元素,返回下一个元素的位置 |
20 | c.erase(beg,end) | 移除区间[beg,end)区间内的所有元素,返回下一个元素的位置 |
21 | c.resize(num) | 将元素数量大小调整为num,如果数量比原始要大,那么多出来的部分用默认构造函数进行初始化 |
22 | c.resize(num,elem) | 将元素数量大小调整为num,如果数量比原始要大,那么多出来的部分用初始化为elem |
23 | c.clear() | 移除所有元素,释放内存 |
异常控制
Deque提供的异常处理是STL所定义的最低限度的异常处理,除了at函数会对边界进行检查,可能会抛出range-error异常之外,其他函数不会抛出任何标准异常,但编译器往往会加入自己的异常处理,比如VS所实现的STL版本就有诸多异常控制。