C++Primer第五版【笔记】——第九章——顺序容器
来源:互联网 发布:华为云计算怎么样 编辑:程序博客网 时间:2024/04/30 07:59
1. 顺序容器概述
表一中的顺序容器,提供了对元素快速的顺序访问。但是其他操作的开销则不同:
- 添加或删除元素的开销
- 进行非顺序访问的开销
插入和删除。除了array是定长容器之外,容器提供了高效的,灵活的内存管理。我们可以添加和删除元素,动态的增长或缩减容器的大小。
容器中元素的存储方法决定了它所支持的操作,以及操作的效率。比如,string和vector中的元素存储在连续的内存中。因此它们的元素是连续的,支持快速随机访问。但是,在元素序列中的插入或删除操作开销很大。list和forward_list则可以快速的在容器中的任意位置插入或删除元素。但是,不支持随机访问。deque是一个比较复杂的数据结构。它可以像string和vector那样,支持快速随机访问。同时可以在容器的头部和尾部快速的插入和删除元素。
【C++11】
forward_list和array是新标准中加入的。array相比内置数组,更加安全、易用。它具有固定长度,不支持插入和删除元素。forward_list可以与最高效的手写单链表相媲美。forward_list不支持size操作,因为该操作开销很大。
【注】
新版本的库容器要比之前版本的效率高的多。所以,尽可能的使用它们吧。
如何选择使用哪个顺序容器?
可以按照下面的规则:
- 使用vector,除非你有理由使用其他容器
- 如果你的程序有许多小元素,且空间开销很重要时,不要使用list或forward_list
- 如果程序需要随机访问元素,使用vector或deque
- 如果程序需要在容器中间插入或删除元素,使用list或forward_list
- 如果程序需要在头部或尾部插入或删除元素,而不是中间,使用deque
- 如果程序只在读入数据时需要在容器中间插入元素,随后需要随机访问元素:
-- 首先,确定是否真的需要在容器中间插入元素。一般先为vector追加元素,然后调用sort函数排序会更容易。
-- 如果真的需要在中间插入元素,考虑在输入阶段使用list。然后当输入完成后,将list复制到vector。
如果程序既需要随机访问,又需要在中间插入或删除元素。那么就需要比较list或forarad_list访问元素的开销,和vector,deque插入或删除元素的开销。同时还需要考虑程序本身是随机访问次数更多还是插入、删除操作更多。或者还可以考虑使用更复杂的数据结构来支持。
2. 容器库概述
容器的操作种类可以划分为一个层次:
- 一些操作是通用的。
Type Aliases Construction Assignment and swap Size Add/Remove Elements
(not valid for array)Note: the interface to these operations varies by container type c.insert(args) 将args指代的元素插入c中 c.emplace(inits) 使用inits构造一个c中的元素 c.erase(args) 删除args指代的元素 c.clear() 删除c中所有元素,返回voidEquality and Relational Operators Obtain Iterators Additional Members of Reversible Containers (not valid for forward_list) reverse_iterator Iterator that addresses elements in reverse order const_reverse_iterator Reverse iterator that cannot write the elements c.rbegin(), c.rend() Return iterator to the last, one past the first element in c c.crbegin(), c.crend() Return const_reverse_iterator - 顺序容器,关系容器,非顺序容器的其他操作
Defining and Initializing Containers
(必须是相同类型的容器,且其中的元素类型也相同;对于array,长度也需要相同) C c{a,b,c...} c is a copy of the elements in the initializer list.列表中元素的类型必须与C中元素的类型是相容的。对于array,列表中元素的个数必须小于或等于array的长度,缺少的部分用值初始化 C c={a,b,c...} C c(b,e)c is a copy of the elements in the range denoted by iterators b and e. 元素类型必须和C的元素类型相容。(不适用于array)Constructors that take a size are valid for
sequential containers only(not including array) C seq(n) seq has n value-initialized elements; the constructor is explicit.(Not valid for string). C seq(n, t) seq has n elements with value t.
容器几乎可以保存任何的类型,包括容器本身。但是还是有一些限制的。比如一些容器需要其元素支持某个特定的操作,如果某类型不支持这种操作,那么就不能作为容器的元素。
vector<vector<int>> vvi;vector<vector<int> > vv_i; // 旧版本需要加一个空格
2.1 迭代器
需要特别指出的是,迭代器的范围是一个前开后闭区间:[begin, end)
2.2 容器的类型成员
容器中定义了一些类型成员,比如size_type,iterator, const_iterator等。这些类型别名可以让我们在使用的时候不需要知道具体类型,在泛型编程中很有用。
2.3 begin和end
【C++11】
c版本的begin和end是新标准加入的,使用auto可以很方便的定义。
vector<string> vs;const vector<string> c_vs;auto beg = vs.begin(); // iteratorauto cbeg = c_vs.begin(); // const_iteratorauto cbeg1 = vs.cbegin(); // const_iterator不带c的版本在内部是重载函数。一个const版本的和一个非const版本的。如果是const对象,则调用const版本,如果是非const对象则调用非const版本。
2.4 定义和初始化容器
除了array之外,每个容器都定义了默认构造函数,以及一个指定容器大小和初始化元素值的构造函数。
根据一个容器来初始化另一个容器有两种方法:第一种是直接复制,容器类型和元素类型必须匹配;第二种是用迭代器指定一个范围,容器类型和元素类型都不需要一致,元素类型要保证是可以转换的。
vector<string> vs = {"hello", "world"};list<const char*> lcc = {"ok", "I", "want"};deque<string> ds(vs); // errorforward_list<string> fs(lcc.begin(), lcc.end()); // ok顺序容器的初始化:
vector<int> vi(10, 1024); // 初始化为10个1024list<string> ls(10, "ok"); // 初始化为10个"ok"forward_list<int> fi(10); // 初始化为10个0deque<string> ds(10); // 初始化为10个空string需要指出的是,只提供一个元素个数的构造函数,需要元素本身(如果是类类型)具有默认构造函数。
array
array<int, 50> a = {0, 1, 2 ,3}; // 其余元素为0
2.5 赋值和交换
b and e. The iterators b and e must not refer to elements in seq. seq.assign(il) Replaces the elements in seq with those in the initializer list il. seq.assign(n,t) Replaces the elements in seq with n elements with value t.【WARNING!】Assignment related operations invalidate iterators, references, and pointers into the left-hand container. Aside from string they remain valid after a swap, and (excepting arrays) the containers to which they refer are swapped.
与内置数组不同的是,array支持赋值。
array<int,5> arr1 = {1,2,3,4,5};array<int,5> arr2 = {0};arr1 = arr2;【注】赋值操作的等号左右两边的类型必须一致
使用assign
顺序容器还支持assign操作,提供了更加灵活的赋值,只要类型是相容的(即可以相互转换)。
list<string> ls;vector<const char*> vcc;ls = vcc; // errorls.assign(vcc.cbegin(), vcc.cend()); // ok
使用swap
2.6 size操作
size函数返回当前容器中包含的元素个数;如果容器为空,empty返回ture,否则返回false;max_size返回容器最大可以容纳的元素个数。
2.7 关系操作符
所以容器都支持相等操作(==和!=);所以容器除了无序关联容器,都支持关系操作(>, >=, <, <=)。
【注】只有容器的元素支持相应的关系操作时,才能比较该容器。
3. 顺序容器操作
3.1 添加元素
除了array,所以的库容器都支持容器大小在运行时的动态改变。
c.push_back(t)
c.emplace_back(args)在容器c的尾部,创建一个值为t的元素,或根据args构建一个元素。返回voidc.push_front(t)
c.emplace_front(args)在容器c的头部,创建一个值为t的元素,或根据args构建一个元素。返回voidc.insert(p,t)
c.emplace(p,args)在迭代器p所指元素的前面,插入一个值为t的元素,或根据args构建一个元素;
返回指向插入元素的迭代器c.inesrt(p, n, t)在迭代器p所指元素的前面,插入n个值为t的元素;返回指向插入的第一个元素的迭代器,
如果n为0,返回pc.insert(p, b, e)在迭代器p所指元素的前面,插入迭代器指向范围为[b,e)的元素,b,e不能指向c本身。
返回指向插入的第一个元素的迭代器,如果范围为空,返回pc.insert(p, il)il是包含元素值的括号序列。在迭代器p指向的元素前面插入该序列。返回指向插入的
第一个元素的迭代器,如果序列为空,返回p【注意】在vector, string, deque中插入元素,会使现有的迭代器,引用和指针无效。forward_list不支持push_back和emplace_back。vector和string不支持push_front和emplace_front.
c.emplace_back(args)在容器c的尾部,创建一个值为t的元素,或根据args构建一个元素。返回voidc.push_front(t)
c.emplace_front(args)在容器c的头部,创建一个值为t的元素,或根据args构建一个元素。返回voidc.insert(p,t)
c.emplace(p,args)在迭代器p所指元素的前面,插入一个值为t的元素,或根据args构建一个元素;
返回指向插入元素的迭代器c.inesrt(p, n, t)在迭代器p所指元素的前面,插入n个值为t的元素;返回指向插入的第一个元素的迭代器,
如果n为0,返回pc.insert(p, b, e)在迭代器p所指元素的前面,插入迭代器指向范围为[b,e)的元素,b,e不能指向c本身。
返回指向插入的第一个元素的迭代器,如果范围为空,返回pc.insert(p, il)il是包含元素值的括号序列。在迭代器p指向的元素前面插入该序列。返回指向插入的
第一个元素的迭代器,如果序列为空,返回p【注意】在vector, string, deque中插入元素,会使现有的迭代器,引用和指针无效。forward_list不支持push_back和emplace_back。vector和string不支持push_front和emplace_front.
不同容器的插入操作效率是不同的。比如在vector中插入(非尾部)元素,需要元素的移动操作,这会导致额外开销。
list<string> ls;vector<string> vs = {"this", "is", "for", "test"};ls.insert(ls.begin(), vs.end()-2, v.end());ls.insert(ls.end(), {"love","coding"});【c++11】
新标准中,接受一个计数参数或迭代器范围的insert版本会返回指向插入的第一个元素的迭代器(之前的版本返回void)。
【c++11】
emplace_front, emplace, emplace_back是新标准加入的三个操作,与push_front, insert, push_back不同的是,它们用args参数调用元素类型的构造函数在插入的位置创建新的元素,而不是根据传递的元素复制一个副本。
相当于根据参数类型,调用相应的构造函数,在容器所在的内存空间(插入的位置)创建新的元素。
3.2 访问元素
3.3 删除元素
3.4 forward_list的专用操作
对于单向链表来说,插入或删除元素,会改变其前面一个元素中的指针。所以它的插入和删除操作不同于其他容器。
lst.cbefore_begin() 指向链表开头前一个位置的迭代器,该迭代器指向的不是链表中的某个元素,
不能解引用。cbefore_begin()返回const_iterator lst.insert_after(p,t)
lst.insert_after(p,n,t)
lst.insert_after(p,b,e)
lst.insert_after(p,il) 在迭代器p所指元素的后面插入相应的元素。 emplace_after(p, args) 在迭代器p所指元素的后面,根据args构造一个新的元素 lst.erase_after(p)
lst.erase_after(b,e) 删除在迭代器p所指的,或范围为[b,e)的元素。返回被删除元素的后继的迭代器。
3.5 改变容器大小
容器(除了array)可以使用resize来改变大小。
否则添加新的元素(值初始化) c.resize(n,t) 重新定义c的大小为n。增加的新元素值为t【注】当resize使容器缩小时,指向被删除元素的迭代器、引用和指针会失效;resize会使vector、string、deque的所以迭代器、引用和指针失效。
如果容器保存的是类类型元素,当resize使得增加元素时,会调用该类的默认构造函数,如果没有默认构造函数,则必须提供与构造函数相应的初始化值。
4. vector如何增长
vector的内存管理是,根据一定的策略事先分配一段内存空间,当元素个数增长到空间不够用时,再分配新的内存空间,然后将元素搬到新的空间,再释放旧的空间。
【c++11】
新标准加入的shrink_to_fit使得可以释放容器中多余的容量。适用于deque, vector和string。
增长测试:
#include <iostream>#include <vector>using namespace std;int main(){vector<int> vi;cout << "size: " << vi.size() << "; capacity: " << vi.capacity() << endl;for(vector<int>::size_type idx = 0; idx <= 50; ++idx) {vi.push_back(idx);cout << "size: " << vi.size() << "; capacity: " << vi.capacity() << endl;}return 0;}
5. string操作
略过。
6. 容器适配器
适配器是一个通用概念。包括容器适配器,迭代器适配器和函数适配器。适配器是一种使一种对象按另一种对象的规则操作的机制。
除了前面几节讲到的顺序容器,C++库还定义了三种顺序容器适配器:stack, queue和priority_queue。
定义一个适配器
默认情况下,stack和queue在deque上实现,priority_queue在vector上实现。
stack<int> stk;也可以指定用来实现的容器:
stack<int, vector<int>> stk_vec实现适配器的容器是有限制的。所有的适配器都要求有添加和删除元素的能力。所以array不能使用。所以的适配器都要求在容器的末尾添加和删除元素,forward_list也不能使用。stack要求push_back, pop_back和back操作,可以用vector, deque, list实现。queue要求back, push_back, front和push_front操作,可以用list, deque实现。priority_queue除了要求front, push_back和pop_back 操作,还要求随机访问,所以可以用vector 或 deque实现。
- C++Primer第五版【笔记】——第九章——顺序容器
- C++primer第五版笔记-第九章顺序容器
- 实验测试1《C++ Primer》第五版——第九章 顺序容器
- 读书笔记《C++ Primer》第五版——第九章 顺序容器
- 实验测试2《C++ Primer》第五版——第九章 顺序容器
- 顺序容器——C++Primer第九章
- C++ primer笔记——第九章 顺序容器
- 《C++ Primer》读书笔记——第九章_顺序容器
- 《C++primer(第五版)》学习之路-第九章:顺序容器
- c++ primer(第五版)笔记 第九章 顺序容器(1)
- c++ primer(第五版)笔记 第九章 顺序容器(2)
- c++ primer(第五版)笔记 第九章 顺序容器(3)
- c++ primer(第五版)笔记 第九章 顺序容器(4)
- 记录——《C Primer Plus (第五版)》第九章编程练习第九题
- c++primer学习笔记 ( 第九章 顺序容器)
- C++primer 第九章:顺序容器----字符串
- 【c++ primer 笔记】第九章 顺序容器
- C++primer第4版第九章顺序容器
- SQL 数据库Sa账号密码不能更改问题
- Windows7+WDK+VS2010+VisualDDK驱动开发环境搭建
- linux sys属性节点创建
- asp.net跳转页面的三种方法比较
- DOTNETBAR的使用方法(转)
- C++Primer第五版【笔记】——第九章——顺序容器
- 谷歌面试题:给定能随机生成整数1到5的函数,写出能随机生成整数1到7的函数
- Spring声明式事务配置管理方法
- 开机登录时取消Ctrl+Alt+Delete
- 数学之美番外篇:平凡而又神奇的贝叶斯方法
- simple_hash_list
- ArcGIS Server .NET ADF中的AJAX之深入浅出/CallbackResult详解
- org.hibernate.hql.ast.QuerySyntaxException: UserType is not mapped
- gcd以及ex_gcd的总结