顺序容器:为程序提供控制元素存储和访问顺序的能力,这种顺序与元素加入到容器时的位置相对应,而与元素值无关。
另外还有根据关键字的值来存储元素的容器:有序、无序关联容器。
另外STL还有三种容器适配器,用来与容器类型适配。
顺序容器包括
vector | 可变大小数组 |
deque | 双端队列,支持随机访问,在front/back插入、删除速度很快 |
list | 双向链表,只支持顺序访问,在任何位置插入元素很快 |
forward_list | 单向链表,只支持单向顺序访问 |
array | 固定大小数组,支持随机访问,不能添加、删除元素 |
string | 与string相似的容器,专门用与保存字符,随机访问块,在尾部插入、删除速度快 |
除array外,其他的绒里都提供高效、灵活的内存管理(改变内存中的元素,数量,改变容器的大小)。
string/vector将元素存放在连续的内存空间中,可以随机访问,但是添加、删除元素的效率低。
list和forward_list 添加、删除元素都非常快,代价是不支持随机访问(为了访问某一个元素,需要遍历整个容器)
deque支持快速的数据访问,与string/vector相似,在中间为止添加/删除元素代价可能很高,但是在首尾位置添加/删除元素很快。
容器支持、
类型别名 解释
iterator 此容器类型的迭代器类型,就是下标|指针
const_iterator 可以读取元素,但是不能修改元素的迭代器类型
size_type 无符号类型,足够保存此种容器类型的最大可能容器的大小
difference_type 带符号整数类型,足够宝尊两个迭代器之间的距离
value_type 元素类型
reference 元素的左值类型,与value_type& 类型相同
const_reference 元素的const左值类型,即 const value_type &
=========================
构造函数 解释
C c; 默认构造函数,构造函数
C c1(c2);
C c(b,e);不支持array
C c{a,b,c...} 列表初始化c
=========================
赋值 与swap
c1 = c2;
c1 = {a,b,c...}
a.swap(b)
swap(a,b)
=======================
大小
c.size();不支持forward_list
c.max_size()
c.empty()
====================
添加删除元素/不适用于array
c.insert(args)
c.emplace(inits);使用inits构造c中的一个元素
c.erase(args);删除args制定的元素
c.clear() ;删除c中所有的元素,返回void
=============================
关系运算符
==,!= ;所有容器都支持相等(不等)运算符
<,<=,>,>= ;关系运算符(无序关联容器不支持)
==================================
获取迭代器
c.begin(),c.end()
c.cbegin(),cend(),返回const_iterator
=====================
反向容器的额外成员(不支持forward_list)
reverse_iterator
const_reverse_iterator
c.rbegin(),c.rend();//返回指向c的尾元素和首元素之前位置的迭代器
c.crbegin(),c.crend()
9.2.3 begin和 end 成员
当不需要写访问时,应使用cbegin和cend成员
9.2.4容器定义和初始化
C c;
C c1(c2)
C c1 = c2;
C c{1,2,3...}
C c={1,2,3,...}
C c(begin,end);
---------只有顺序容器的构造函数才能接收大小参数
C seq(n)
C seq(n,t)
9.2.5
容器赋值运算
c1 = c2;
c = {1,2,c...}
swap(c1,c2);交换c1/c2中的元素,c1/c2必须具有相同的类型;swap通常比c2向c1拷贝元素快
c1.swap(c2);
assign操作不适用于关联容器和array
seq.assign(b,e)//
seq.assign(il)//将seq中的元素替换为初始化列表il中的元素
seq.assign(n,t)
####note: swap只是交换了两个容器的内部数据结构,除array外,swap不对元素进行拷贝,删除和插入操作,可以保证在O(1)时间内完成。
9.3
向vector/string/deque插入元素会使所有指向容器的迭代器iterator,引用reference,指针pointer失效,所以在跟新了容器的元素时,都应该更新迭代器,引用,指针。
在一个vector/string的尾部之外的任何位置,或是deque的首尾之外的任何位置天价了元素,都需要移动元素,而且向一个vector/string添加元素可能引起整个对象存储空间的重新分配。重新分配一个对象的存储空间需要分配新的内存,并将元素从旧的空间移动到新的空间中。
容器元素是拷贝
当文用一个对象来初始化容器时,或将一个对象插入倒容器中时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身。就像我们将一个对象传递给非引用参数一样,容器中的元素与提供值的对象之间没有任何关联了。随后对容器中元素任何改变都不会影响到原始对象,反之依然。
使用emplace操作,新标准引入了三个新成员emplace_front,emplace,emplace_back,这些操作不是构造而是拷贝元素,这些操作对应push_back,insert,push_back,允许我们将元素放置在容器头部,一个指定位置或容器头部
9.3.2访问元素
包括array在内的每个顺序容器都有一个front(),
除了forward_list外,每个顺序容器都有一个back(),分别返回首尾元素
在顺序容器中访问元素的操作
at和下标操作符只适用于string,vector,deque,array容器,back不能用于forward_list
c.back()
c.front()
c[n]
c.at[n]
访问成员函数返回都是引用,back,front,at,[]返回的都是引用,如果容器是一个const对象,则返回const引用。如果容器不是const,则返回值是普通引用,我们可以引用它改变元素的值:
if(!c.empty()){ c.front() = 42; auto &v = c.back(); v = 1024; auto v2 = c.back();//如果我们使用auto变量来保存这些函数的值,并且希望使用此变量来改变元素的值,必须记得将变量定义为引用类型 auto &v = c.back(); v = 0; }
9.3.3
顺序容器的删除操作
这些操作会改变容器的大小,不能用于array
forward_list 有特殊的erase,forward_list不支持pop_back;
vector/string 不支持pop_back
c.pop_back()
c.pop_front()
c.earse(p)
c.erase(b,e);
c.clear()
9.3.4 特殊的forward_list操作
forward_list当添加/删除一个元素时,会改变该元素之前、之后的元素指针;
forward_list没有定义insert,emplace,erase,而是定义了insert_after,emplace_after和erase_after的操作,所以在删除元素之前应该先知道这个元素的首前迭代器;
#################################
泛型算法:
算法如何工作
find的工作实在一个未排序的元素序列中寻找一个特定的元素,执行的步骤:
1,访问序列中的首元素
2,比较此元素与我们要查找的元素
3,如果此元素与我们要查找的值匹配,find返回标识此元素的值
4,否则,find前进到下一个元素,重复执行步骤2,3,
5,如果到达序列末尾,find停止
6,如果find到达序列末尾,他应该返回一个指出元素未找到的值。此值和步骤3返回的值必须具有相容的类型。
============
初始泛型算法:STL中提供了超过100个算法,有一致的结构,除了少数例外,每个算法都对一个范围内的元素进行操作,此范围就是“输入范围”,前闭后开【)。我们可以根据算法是否读取元素,改变元素,或者重排元素位置来进行区分。
##只读算法
find(vec.cbegin(),vec.end(),val)
accumulate(vec.cbegin(),vec.cend(),0);
对于只读而不改变元素的算法,通常最好使用cbegin()和cend();但是有时候根据需要,也需要使用begin(),end();
equal(vec.cbegin(),vec.cend(),vec2.cbegin()); equal假设:第二个序列至少与第一个序列一样长,
##写容器元素的算法
算法不会执行容器操作,因此他们本身不可能改变容器的大小。但是会修改容器的内容。
fill(vec.begin(),vec.end(),0);
算法不要求比较的容器都相同,但是他们的容器类型都必须相同。equal(vec.begin,vec.end(),list1.begin());ok
插入迭代器back_inserter(),定义在头文件iterator中的一个函数;接受一个指向容器的引用,返回一个与该容器绑定的插入迭代器。
vector<int> vec; auto it = back_inserter(vec); *it = 32;
拷贝算法copy,另一个向目的迭代器指向的输出序列中的元素 写入数据的算法。 接受三个迭代器,前两个是输入范围,第三个表示目的序列的起始位置。
int a[] = {1,2,3,4,5,6,7}; int a2[sizeof(a)/sizeof(*a)];a2与a大小一样 auto ret = copy(begin(a),end(a),a2);//ret = a2.end()
note:copy(begin(a1),end(a1),a2) a2的大小要与a1一样长
replace_copy(ilsit.begin(),ilist.end(),0,2);将ilist中所有的0改变为2
##重排容器元素的算法
sort,会重排输入序列中的元素,使之有序,利用元素类型<(小于)运算符来实现的
unique, 消除重复单词,首先将vector排序,使得重复的单词相邻出现。返回的位置是 一个指向 不重复值 范围末尾的迭代器。
#include <iostream> #include <vector> #include <algorithm> using namespace std; void dlimDups(vector<string> &words){ sort(words.begin(),words.end()); for(auto i: words){ cout<<i<<" "; }cout<<endl; auto end_unique = unique(words.begin(),words.end()); for(auto i: words){ cout<<i<<" "; }cout<<endl; words.erase(end_unique,words.end()); for(auto i: words){ cout<<i<<" "; }cout<<endl; } int main() { vector<string> vstr = {"the","quick","red","fox","jumps","over","the","slow","red","turtule"}; cout<<"vstr.length = "<<vstr.size()<<endl; for(auto i:vstr){ cout<<i<<" "; }cout<<endl; dlimDups(vstr); return 0; }
##定制操作算法
sTL允许我们定义自己的操作来代替默认运算符,sort算法默认的比较操作符是<,我们可以定义自己的操作顺序>,>=等。
我们可以向算法传递函数,或者lambda表达式;
如果我们希望单词排序程序输出,先按长度排序,大小相同的再按字典顺序来排。
定义sort(vec.begin,vec.end(),predicate),
predicate谓词,STL算法使用的谓词分为两类:一元谓词(接受一个参数),二元谓词(有两个参数);由于接受谓词参数的算法对输入序列中的元素调用谓词,那么元素类型必须能转换为谓词的参数类型。
bool isShorter(const string &s1, const string &s2){ return s1.size() < s2.size(); }
这个二元谓词函数,在输入序列中所有可能的元素值上面定义了一个一致的序。
sort(words.begin(),words.end(),isShorter);//按照长度由短至长排序。
在我们words是按照大小排序的同时,如果还需要长度的元素按字典序排序。这是我们需要使用稳定排序算法: stable_sort(words.begin(),words.end(),isShorter);
lambada表达式以后再说。
===========================================================
第11章定义关联型容器
map/set容器
map中的元素是key-value对;set中每个元素只包含一个关键字;
set支持高效关键字查找操作---检查一个给定关键字是否在set中,可以定义忽略掉的单词等。
标准库中提供了8个关联容器,体现在3个不同的维度上:
1,或者是一个set,或者是一个map |
2,或者要求不重复关键字,或者允许重复关键字 |
3,按顺序保存元素,或无序保存元素; |
具体的类型有:
map/set/mutlimap/multiset/
unordered_map/unordered_set/
unordered_multimap/unordered_multiset
11.2 关联容器概述
定义关联容器:
map<string,size_t> word_count;
set<string> excluds = {"1","b","c"};
map<string,string> author = {{"b","b_value"},{"c","c_value"}};
初始化multimap/multiset,一个map/set中的关键字必须是唯一的,即对于一个给定的关键字,只能有一个元素的关键字对应它。
vector<int> ivec;
for(vector<int>::size_type i = 0;i != 10;i++){
ivec.push_back(i);ivec.push_back(i);
}
set<int> iset(ivec.cbegin(),ivec.cend());
11.2.2 关键字类型的要求
默认情况下,STL使用关键字类型的小于符号来比较两个关键字;在set类型中,关键字类型就是元素类型;在map类型中,关键字类型是元素的第一部分的类型。
可以向一个算法提供我们自己定义的比较操作,也可以定义自己定义的操作来代替关键字小于运算服。所提供的操作必须在关键字类型上定义一个严格弱序,这个严格弱序不一定是“小于等于”。
使用关键字类型的比较函数,为了指定使用自定义的操作,必须在定义关联容器类型时提供此操作的类型,用尖括号支出要定义的哪种类型的容器,自定义的操作类型必须在尖括号紧跟着元素类型给出。
bool compareIsbn(const Sales_data &lhs, const Sales_data &rhs){
return lhs.isbn() < rhs.isbn();
}
multiset<Sales_data, decltype(compareIsbn)*> bookstore(compare) --->C++ primer chinese Page379
11.2.3 pair type
#include <utility>
pair<type_1,type_2> mypair;
pair<T1,T2> p; pair<T1,T2> p(v1,v2); pair<T1,T2> p = {v1,v2}; make_pair(v1,v2); p.first p.second p1 relop p2// <, > ,<=, >= //c++ primer 5th Page 380 p1 == p2 p1 != p2
11.3
key_type 容器的关键字类型
mapped_type 每个关键字关联的类型,只用于map,就是值类型;例如,map<string,int>::mapped_type v5;//v5 is int
value_type 对于set来说,它与keytype相同;对于map来说, is pair<const key_type,mapped_type>
定义关联型迭代器,当解引用一个关联容器迭代器时,我们会得到一个类型为容器的value_type,对于map来说,value—type是一个pair类型,其first成员是一个const关键字,但是second不是const的
另外set的迭代器是const,就是不能利用解引用符号来修改set中的元素值。例如×set—it = 42,是不允许的,因为set—it是一个只读类型
关联容器和算法,通常我们不对关联容器使用泛型算法。const意味着不能将关联容器传递给修改或重排容器元素的算法,因为这类算法需要向元素写入值,set类型中的元素是const,map中的元素是pair,但是第一个元素是const;
so,关联容器只能用于读取元素的算法,但是很多这类算法,需要搜索序列,由于关联容器中的元素不能通过他们的关键字进行快速查找,因此对其使用泛型算法没有什么意思。但是关联容器总是有一个find成员,通过一个给定的关键字来直接获取元素。
11.3.2 添加元素
利用insert函数,或者emplace函数,
对于set来说,插入重复数据,没有什么影响;
对于map来说,需要插入的pair类型,因此需要利用原始数据创建pair;insert的返回值是一个pair,pair的first成员是一个迭代器,指向具有给定关键字的元素,second成语根据是否是新插入的元素返回true(是)/false(原来就有的)
初级展开语句
ret
ret.first
ret.first->
ret.first->second
++ret.first->second = ++((ret.first)->second);
11.3.3 delete elements
c.erase(k);//kye = k, return size_type, is a quantity of deleted elements
c.erase(p); // p is a iterator, is the next element of deleted elemtent
c.erase(b,e)
11.3.4 map index control
there is not [] in set.
11.3.5 access elements
find();