《C++ Primer》 Part II(Containers and Algorithms)
容器的构造函数:
C<T> c; //默认构造函数,通用 C<T> c(c2); //创建容器 c2 的副本,通用 C<T> c(b, e); //根据迭代器 b, e 的范围(不包括e)生成容器,通用 C<T> c(n, t); //使用 n 个值为 t 的元素生成容器,只适用于顺序容器 C<T> c(n); //创建 n 个初始化元素的容器,只适用于顺序容器。采用这种类型的初始化,元素类型必须是内置或复合类型,或者是提供了默认构造函数的类类型。如果元素类型没有默认构造函数,则必须显式指定其元素初始化式。
我们知道指针就是迭代器,因此允许通过使用内置数组中的一对指针初始化容器也就不奇怪了:
char *words[] = {"stately", "plump", "buck", "mulligan"}; size_t words_size = sizeof(words)/sizeof(char *); list<string> words2(words, words + words_size);
第二个指针提供停止复制的条件,其所指向的位置上存放的元素并没有复制。
容器内元素的类型约束:
支持复制和赋值功能是容器元素类型的最低要求。此外,关联容器的键类型还需满足其他的约束;
引用不支持一般意义的赋值运算,IO 库类型不支持复制或赋值。因此,不能创建引用类型或IO类型对象的容器。
此外,auto_ptr 类型也不支持。
将一个容器复制给另一个容器时,类型必须匹配:容器类型和元素类型都必须相同。
list<int> lt(3,10); list<int> lt2(lt); //OK //vector<int> vect(lt); //Error! vector<int> vect(lt.begin(),lt.end()); //OK for(vector<int>::const_iterator it = vect.begin();it != vect.end();it++) { cout<<*it<<endl; }
只有vector 和 deque 类型迭代器支持的操作:
iter + n , iter -n
iter1 - iter2
>, >=, <, <=
因为只有这种两种容器为其元素提供快速、随机的访问,它们确保可根据元素位置直接有效地访问指定的容器元素。
list<int> lt(3,10); vector<int> vect(3,10); //cout<<*(lt.begin()+1)<<endl; //Error cout<<*(vect.begin()+1)<<endl; //cout<<lt.end()-lt.begin()<<endl; //Error cout<<vect.end()-vect.begin()<<endl; /* //Error if(lt.end() >= lt.begin()) { cout<<"true"<<endl; } */ if(vect.end() >= vect.begin()) { cout<<"true"<<endl; }
list 容器的迭代器只提供前置和后置的自增、自减运算以及相等(不等)运算。
所有容器都支持的类型名称:
size_type //无符号整型,足以存储此容器类型的最大可能容器长度 iterator //此容器类型的迭代器类型 const_iterator //元素的只读迭代器类型 reverse_iterator //按逆序寻址元素的迭代器 const_reverse_iterator //元素的只读(不能写)逆序迭代器 difference_type //足够存储两个迭代器差值的有符号整型,可为负数 value_type //元素类型 reference //元素的左值类型,是 value_type& 的同义词 const_reference //元素的常量左值类型,等效于 const value_type&
最后三种类型使程序员无须直接知道容器元素的真正类型,就能使用它。需要使用元素类型时,只要用 value_type 即可。如果要引用该类型,则通过 reference 和 const_reference 类型实现。在程序员编写自己的泛型程序(第十六章)时,这些元素相关类型的定义非常有用。
容器的 begin 和 end 操作:
c.begin() //返回一个迭代器,它指向容器 c 的第一个元素 c.end() //返回一个迭代器,它指向容器 c 的最后一个元素的下一位置 c.rbegin() //返回一个逆序迭代器,它指向容器 c 的最后一个元素 c.rend() //返回一个逆序迭代器,它指向容器 c 的第一个元素前面的位置
上述每个操作都有两个不同版本:取决于容器是否为 const。如果容器不是 const,则这些操作返回 iterator 或 reverse_iterator 类型。如果容器是 const,则其返回类型是 const_iterator 和 const_reverse_iterator 类型。
容器元素都是副本,添加元素的时候,是将元素值复制到容器里。就是说容器中存放的是原始元素的副本。被复制的原始值与新容器中的元素各不相关,此后,容器内元素值发生变化时,被复制的原值不会受到影响,反之亦然。
vector<string> vect; string s = "hello"; vect.push_back(s); vect[0] = "df"; cout<<vect[0]<<endl; //"df" cout<<s<<endl; //"hello"
在顺序容器中添加元素的操作:
c.push_back(t) //只适用于 list 和 deque 容器类型. c.push_front(t) //在容器 c 的前端添加值为 t 的元素。返回 void 类型。<只适用于 list 和 deque 容器类型> c.insert(p,t) //在迭代器 p 所指向的元素前面插入值为 t 的新元素。返回指向新添加元素的迭代器 c.insert(p,n,t) //在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型 c.insert(p,b,e) //在迭代器 p 所指向的元素前面插入 n 个值为 t 的新元素。返回 void 类型
insert(p,t) 函数返回指向新插入元素的迭代器,可使用该返回值在容器中的指定位置重复插入元素:
list<string> lst; list<string>::iterator iter = lst.begin(); while (cin >> word) iter = lst.insert(iter, word); // same as calling push_front
任何 insert 或 push 操作都可能导致迭代器失效。当编写循环将元素插入到 vector 或 deque 容器中时,程序必须确保迭代器在每次循环后都得到更新。
不要存储 end 操作返回的迭代器。添加或删除 deque 或 vector 容器内的元素都会导致存储的迭代器失效。
容器类型和元素类型相同的容器支持用关系操作符进行比较,规则如下:
如果两个容器具有相同的长度而且所有元素都相等,那么这两个容器就相等;否则,它们就不相等。
如果两个容器的长度不相同,但较短的容器中所有元素都等于较长容器中对应的元素,则称较短的容器小于另一个容器。
如果两个容器都不是对文的初始子序列,则它们的比较结果取决于所比较的第一个不相等的元素。
但如果元素类型没有定义关系运算,则此类容器也不能进行关系运算。
顺序容器的大小操作:
c.size() //返回容器 c 中的元素个数。返回类型为 c::size_type c.max_size() //返回容器 c 可容纳的最多元素个数,返回类型为 c::size_type c.empty() // 返回标记容器大小是否为 0 的布尔值 c.resize(n) //调整容器 c 的长度大小,使其能容纳 n 个元素,如果 n < c.size(),则删除多出来的元素;否则,添加采用值初始化的新元素 c.resize(n,t) //调整容器 c 的长度大小,使其能容纳 n 个元素。所有新添加的元素值都为 t
resize 操作可能会使迭代器失效。在 vector 或 deque 容器上做 resize操作有可能会使其所有的迭代器都失效。
对于所有的容器类型,如果 resize 操作压缩了容器,则指向已删除的元素迭代器失效。
访问元素:
c.front() //返回容器 c 的第一个元素的引用。如果 c 为空,则该操作未定义 c.back() //返回容器 c 的最后一个元素的引用。如果 c 为空,则该操作未定义 c[n] //返回下标为 n 的元素的引用。如果 n <0 或 n >= c.size(),则该操作未定义。<只适用于 vector 和 deque 容器> c.at(n) //同上 <只适用于 vector 和 deque 容器>
使用front() 或 back() 方法,请务必保证容器不为空。另外,可以使用begin() 和end()-1 方法解引用来得到同样的结果。
vector<int> vect; for(int i= 0;i<5;i++) vect.push_back(i); if(vect.size()>0) { cout<<vect.front()<<endl; //第一个元素 cout<<*(vect.begin())<<endl; //第一个元素 cout<<vect.back()<<endl; //最后一个元素 cout<<*(vect.end()-1)<<endl; //最后一个元素 }
删除顺序容器内的元素:
c.pop_front() //删除容器 c 的第一个元素。返回 void。如果 c 为空容器,则该函数未定义。 <只适用于 list 或 deque 容器> c.pop_back() //删除容器 c 的最后一个元素。返回 void。如果 c 为空容器,则该函数未定义 c.erase(p) //删除迭代器 p 所指向的元素。返回一个迭代器,它指向被删除元素后面的元素。如果 p 指向容器内的最后一个元素,则返回的迭代器指向容器的超出末端的下一位置。如果 p 本身就是指向超出末端的下一位置的迭代器,则该函数未定义 c.erase(b,e) //删除迭代器 b 和 e 所标记的范围内所有的元素。返回一个迭代器,它指向被删除元素段后面的元素。如果 e 本身就是指向超出末端的下一位置的迭代器,则返回的迭代器也指向容器的超出末端的下一位置 c.clear() //删除容器 c 内的所有元素。返回 void
pop_front 操作通常与 front 操作配套使用,实现以栈的方式处理容器:
while (!ilist.empty()) { process(ilist.front()); // do something with the current top of ilist ilist.pop_front(); // done; remove first element }
这个循环非常简单:使用 front 操作获取要处理的元素,然后调用 pop_front 函数从容器 list 中删除该元素。
顺序容器的赋值操作:
c1 = c2 //删除容器 c1 的所有元素,然后将 c2 的元素复制给 c1。c1 和 c2 的类型(包括容器类型和元素类型)必须相同 c1.swap(c2) //交换两个容器,事实上两个容器内的元素并没有移动,只是容器名交换了。
c.assign(b,e) //重新设置 c 的元素:将迭代器 b 和 e 标记的范围内所有的元素复制到 c 中。 //书上说:b 和 e 必须不是指向 c 中元素的迭代器。但测试是可以的! c.assign(n,t) //将容器 c 重新设置为存储 n 个值为 t 的元素
由于 assign 操作首先删除容器中原来存储的所有元素,因此,传递给 assign 函数的迭代器不能指向调用该函数的容器内的元素。
但是,代码中却是可以,WHY?
第三种方法比第一种方法通用性更广,如可通过 assign 操作实现将 vector 容器中一段 char* 类型的元素赋给 string 类型 list 容器。
swap操作可以节省删除元素的成本:该操作不会删除或插入任何元素,而且保证在常量时间内实现交换。由于容器内没有移动任何元素,因此迭代器不会失效。它们指向同一元素,就像没作 swap 运算之前一样。虽然,在 swap 运算后,这些元素已经被存储在不同的容器之中了。例如,在做 swap 运算之前,有一个迭代器 iter 指向 svec1[3] 字符串;实现 swap 运算后,该迭代器则指向 svec2[3] 字符串(这是同一个字符串,只是存储在不同的容器之中而已)。
vector 容器的自增长
vector 元素是连续存储的,我们可能会认为,在添加新元素时,vector 会重新分配存储空间,用来存放原来的元素和以及新增的元素,最后辙销旧的存储空间。但事实上,为了使 vector 容器实现快速的内存分配,其实际分配的容量要比当前所需的空间多一些。vector 容器预留了这些额外的存储区,用于存放新添加的元素。于是,不必为每个新元素重新分配容器。所分配的额外内存容量的确切数目因库的实现不同而不同。比起每添加一个新元素就必须重新分配一次容器,这个分配策略带来显著的效率。事实上,其性能非常好,因此在实际应用中,比起 list 和 deque 容器,vector 的增长效率通常会更高。
那么,vector 预留的空间是多大呢?可以通过其成员函数 capacity() 来查看。那么,如果不满意这个预留大小怎么办呢?可以通过其成员函数 reserve(int i) 来设置。
默认情况下,当 vector 增长达到预留空间大小后,分配新的存储空间时,会以加倍当前容量的分配策略实现重新分配。
标准库提供了三种顺序容器适配器:queue、priority_queue 和 stack
使用适配器时,必须包含相关的头文件:
#include <stack> // stack adaptor #include <queue> // both queue and priority_queue adaptors
所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。
stack<int> stk1(); deque<int> deq(10,3); stack<int> stk2(deq);
默认的 stack 和 queue 都基于 deque 容器实现,而 priority_queue 则在 vector 容器上实现。那如果要用 stack 适配 vector 怎么办呢?可以在创建适配器时,通过将一个顺序容器指定为适配器的第二个类型实参,来覆盖其关联的基础容器类型:
vector<int> vect(10,3); //stack<int> stk(vect); //Error! stack<int,vector<int> > stk(vect);
对于给定的适配器,其关联的容器还必须满足一定的约束条件。stack 适配器所关联的基础容器可以是任意一种顺序容器类型。因此,stack 栈可以建立在 vector、list 或者 deque 容器之上。而 queue 适配器要求其关联的基础容器必须提供 pop_front 运算,因此只能建立在 list、deque 容器上,而不能建立在 vector 容器上。priority_queue 适配器要求提供随机访问功能,因此可建立在 vector 或 deque 容器上,但不能建立在 list 容器上。
vector<int> vect(10,3); queue<int,vector<int> > que(vect); //这一步不报错 //que.pop(); //这一步就会报错了,没有pop_front()方法
栈容器适配器(stack)支持的操作:
s.empty() //如果栈为空,则返回 true,否则返回 stack s.size() //返回栈中元素的个数 s.pop() //删除栈顶元素的值,但不返回其值 s.push(item) //在栈顶压入新元素 s.top() //返回栈顶元素的值,但不删除该元素
22、队列(queue)和优先队列适配器(priority_queue)支持的操作:
q.empty() //如果队列为空,则返回 true,否则返回 false q.size() //返回队列中元素的个数 q.pop() //删除队首元素,但不返回其值 q.push(item) //对于 queue,在队尾压入一个新元素,对于 priority_quue,在基于优先级的适当位置插入新元素 q.front() //返回队首元素的值,但不删除该元素<该操作只适用于队列> q.back() //返回队尾元素的值,但不删除该元素<该操作只适用于队列> q.top() //返回具有最高优先级的元素值,但不删除该元素<该操作只适用于优先级队列>
23、使用 pair 类型需要引入头文件: #include <utility>
pair<int,string> p1; //默认构造函数会对其成员采取值初始化,此处为: p1.first = 0; p1.second = ""; p1.first = 3; //分别赋值 p1.second = "hello"; //分别赋值 p1 = make_pair(4,"hi"); //一起赋值。make_pair(first,second) 可以返回一个与元素类型相应的 pair 对象 pair<int,string> p2(10,"world"); typedef pair<int,string> Pair; //使用 typedef 简化写法 Pair p3(1,"a"); Pair p4(2,"b");
map 的 value_type 是存储元素键值的 pair 类型,且键为 const 。如上面的 m 的 value_type 就是 pair<const string, string> 类型。
map 的键类型为 key_type ,值类型为 mapped_type 。
如:cout<<typeid(map<string,string>::mapped_type).name()<<endl;
另外 map 的构造函数只有那三种容器通用的:map<k, v> m; map<k, v> m(m2); map<k, v> m(b, e);
使用下标给map某一项赋值时,如果不存在此键值,则添加;如果存在,则更新。
使用 insert 方法:
m.insert(e) //e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则插入一个值为 e.second 的新元素;如果该键在 m 中已存在,则保持 m 不变。该函数返回一个 pair 类型对象,包含指向键为 e.first 的元素的 map 迭代器,以及一个 bool 类型的对象,表示是否插入了该元素 m.insert(beg, end) //beg 和 end 是标记元素范围的迭代器,其中的元素必须为 m.value_type 类型的键-值对。对于该范围内的所有元素,如果它的键在 m 中不存在,则将该键及其关联的值插入到 m。返回 void 类型 m.insert(iter, e) //e 是一个用在 m 上的 value_type 类型的值。如果键(e.first)不在 m 中,则创建新元素,并以迭代器 iter 为起点搜索新元素存储的位置。返回一个迭代器,指向 m 中具有给定键的元素
下标赋值和insert方法赋值最大的差别就是,当键已存在时,前者会更新对应的值,后者会忽略,不做任何操作。另外,后者显得更紧凑。
m.insert(map<string, string>::value_type("aaa","AAA")); m.insert(make_pair("aaa","AAA")); //或者: typedef map<string,string>::value_type valType; m.insert(valType("aaa","AAA"));
insert 方法返回值:
//insert(e):返回的是一个 pair 对象,其 first 为所插入键在map中最终键值对的迭代器,second 为插入是否成功。 pair<map<string,string>::iterator, bool> ret = m.insert(make_pair("aaa","AAA")); cout<<(ret.first)->first<<endl; //必为 "aaa" cout<<(ret.first)->second<<endl; //如果map中原本没有"aaa"这个键,则这里为新插入的 "AAA";如果map中原本有这个键,则这里为这个键对应的值。 cout<<ret.second<<endl; //插入成功返回1,失败返回0。影响成功的因素:该键是否已存在。 //m.insert(beg, end):返回 void。 //m.insert(iter, e):貌似 iter 就是返回之前定义的 iter 所对应的键值的新的迭代器。即键值还那个,只是迭代器可能变化了,因为可能成功插入一个新的键值对。还是尽量不用它了~~
++ret.first->second; 这个优先级是怎样的呢?实际上就是 ++((ret.first)->second); 还是要把运算符优先级表好好研究一下啊~~
使用下标访问map中的数据很危险,因为如果该键不存在的话,会插入新的键值对。无论是赋值还是读取~~
map<string,string> m; m.insert(make_pair("aaa","aaa")); string str = m["bbb"]; //会插入 make_pair("bbb","") cout<<m["ccc"]; //会插入 make_pair("ccc","")
count(key) 方法来检测某个键是否存在,find(key) 方法可以返回某个键的迭代器,如果不存在则返回 end 迭代器。
if(m.count("ddd")) { cout<<m["ddd"]<<endl; } //如果只是为了读取某个键的值,上面不是一个好方法,因为它查找了两次,可以直接使用 find(key) 方法,找到之后用迭代器来操作即可 map<string,string>::iterator it = m.find("ddd"); if(it != m.end()) { cout<<it->second<<endl; }
除非你真的非常确定你需要使用到下标操作的效果,否则请使用 find(key)方法。
删除 map 中的元素:
m.erase(k) //删除某一个键值对应的元素,如果存在的话。返回size_type 类型的值,表示删除的元素个数。 m.erase(p) //删除某个迭代器指向的元素,请确保该迭代器合法。返回 void m.erase(b, e) //同上,只是这里删除两个迭代器之间的元素。返回 void
set 的构造函数还是那通用的三种,不说了~~
set 跟 map 一样,其键是 const 的,不可改变。
set 也支持 cout(key) 和 find(key) 方法。
插入元素,可以使用 insert(key) 和 insert(b,e) ,前者直接插入某 key ,后者插入迭代器对之间的 key。
set<string> iset; iset.insert("key"); set<string>::iterator set_it = iset.find("key"); *set_it = "key1"; // error
multimap 和 multiset 所支持的操作分别与 map 和 set 的操作相同,只有一个例外:multimap 不支持下标运算。不能对 multimap 对象使用下标操作,因为在这类容器中,某个键可能对应多个值。而因此特性,一些方法上会有一些小区别:
insert 总是会成功的。
erase 会删除对应键的所有元素,并返回删除元素的个数。而带有一个或一对迭代器参数的版本只删除指定的元素,并返回 void 类型。
关联容器 map 和 set 的元素是按顺序存储的,而 multimap 和 multset 也一样。因此,在 multimap 和 multiset 容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。
查找某个键对应的值时,count 函数求出某键出现的次数,而 find 操作则返回第一个查找到的键的实例。
multimap<string,string> mmap; mmap.insert(make_pair("aaa","aaa")); mmap.insert(make_pair("bbb","bbb")); mmap.insert(make_pair("ccc","ccc")); mmap.insert(make_pair("bbb","BBB")); int count = mmap.count("bbb"); multimap<string,string>::const_iterator it = mmap.find("bbb"); for(int i = 0;i != count; it++,i++) { cout<<it->first<<"\t"<<it->second<<endl; }
此外,还有一个优雅简洁的方法:
m.lower_bound(k) //返回一个迭代器,指向键不小于 k 的第一个元素 m.upper_bound(k) //返回一个迭代器,指向键大于 k 的第一个元素 m.equal_range(k) //返回一个迭代器的 pair 对象。它的 first 成员等价于 m.lower_bound(k)。而 second 成员则等价于 m.upper_bound(k)
上述操作适用于所有的关联容器,也可用于普通的 map 和 set 容器,但更常用于 multimap 和 multiset。
在同一个键上调用 lower_bound 和 upper_bound,将产生一个迭代器范围,指示出该键所关联的所有元素。lower_bound 返回的迭代器指向该键关联的第一个实例,而 upper_bound 返回的迭代器则指向最后一个实例的下一位置。如果该键不在 multimap 中,这两个操作将返回同一个迭代器,指向依据元素的排列顺序该键应该插入的位置。
lower_bound 返回的迭代器不一定指向拥有特定键的元素。如果该键不在容器中,则 lower_bound 返回在保持容器元素顺序的前提下该键应被插入的第一个位置。
multimap<string,string>::const_iterator it1 = mmap.lower_bound("bbb"),it2 = mmap.upper_bound("bbb"); while(it1 != it2) { cout<<it1->first<<"\t"<<it1->second<<endl; it1++; }
或使用 equal_range(k) 方法如下:
pair<multimap<string,string>::iterator,multimap<string,string>::iterator> p = mmap.equal_range("bbb"); while(p.first != p.second) { cout<<p.first->first<<"\t"<<p.first->second<<endl; p.first++; }
find(b,e,v) 查找迭代器 b 到 e (不包括e)之间的等于 v 的值,如果找到则返回v对应的迭代器,找不到则返回e。该方法同样可用于数组:
int ia[6] = {1,2,3,4,5,6}; int i = 7; int *result = find(ia,ia+sizeof(ia),i); cout<<(result != ia+sizeof(ia) ? "yes":"no")<<endl;
使用泛型算法和泛化的算术算法,需要分别引入头文件:
#include <algorithm>
#include <numeric>
只读算法:
accumulate(b,e,v); //依次累加 v 和 b到e之间值,并返回。可用于数值计算,也可用于拼接字符串 find_first_of(b1,e1,b2,e2); //在第一段范围内查找与第二段范围中任意元素匹配的元素,然后返回一个迭代器,指向第一个匹配的元素。如果找不到元素,则返回第一个范围的 end 迭代器。 fill(b,e,v); //fill 带有一对迭代器形参,用于指定要写入的范围,而所写的值是它的第三个形参的副本。执行时,将该范围内的每个元素都设为给定的值。如果输入范围有效,则可安全写入。这个算法只会对输入范围内已存在的元素进行写入操作。 fill_n(b,n,v); //参数包括:一个迭代器、一个计数器以及一个值。该函数从迭代器指向的元素开始,将指定数量的元素设置为给定的值。
back_inserter 函数是迭代器适配器。迭代器适配器使用一个对象作为实参,生成一个绑定在该容器上的插入迭代器。在试图通过这个迭代器给元素赋值时,赋值运算将调用 push_back 在容器中添加一个具有指定值的元素。
//Error! vector<int> vect; fill_n(vect.begin(),10,0); //OK vector<int> vect; fill_n(back_inserter(vect),10,0);
copy(b,e,it); 在it前一个位置开始复制 b与e之间的数据,但如果it是插入迭代器,则如下:
vector<int> vect; list<int> lt(5); vect.push_back(1); vect.push_back(2); vect.push_back(3); copy(vect.begin(),vect.end(),lt.begin()); //copy(vect.begin(),vect.end(),back_inserter(lt)); //如果这里是这样写的话,那最后输出应该是:0,0,0,0,0,1,2,3 list<int>::iterator it = lt.begin(); while(it != lt.end()) { cout<<*it<<endl; //1,2,3,0,0 it++; }
replace(b,e,oldvalue,newvalue); //将序列中特定的值替换为新的值。
replace_copy (b,e,back_inserter(ivec), oldvalue, newvalue); //这个算法接受第三个迭代器实参,指定保存调整后序列的目标位置。这样原容器则没有改变。
unique(b,e); //去除重复元素。unique 返回的迭代器指向超出无重复的元素范围末端的下一位置。其实该函数并没有“删除”元素,而是将无重复的元素复制到序列的前端,从而覆盖相邻的重复元素。
lt.sort(); //list容器直接调用sort()方法
stable_sort(b,e,fun); //第三个形参:比较元素所使用的谓词函数的名字。这个谓词函数必须接受两个实参,实参的类型必须与元素类型相同,并返回一个可用作条件检测的值。
bool fun(string str1,string str2) { return str1.size() > str2.size(); //如果这里是 < 号,则排序相反 } int main() { vector<string> vect; vect.push_back("aa"); vect.push_back("a"); vect.push_back("aaa"); stable_sort(vect.begin(),vect.end(),fun); //"aaa","aa","a" }
count_if(b,e,fun); //count_if 算法返回使谓词函数返回条件成立的元素个数。
int fun(string str) { return str.size() < 3; } int main() { vector<string> vect; vect.push_back("aa"); vect.push_back("a"); vect.push_back("aaa"); cout<<count_if(vect.begin(),vect.end(),fun); //2 }
另外三种迭代器:都在 iterator 头文件中定义。
插入迭代器:这类迭代器与容器绑定在一起,实现在容器中插入元素的功能。
iostream 迭代器:这类迭代器可与输入或输出流绑定在一起,用于迭代遍历所关联的 IO 流。
反向迭代器:这类迭代器实现向后遍历,而不是向前遍历。所有容器类型都定义了自己的 reverse_iterator 类型,由 rbegin 和 rend 成员函数返回。
back_inserter 函数是一种插入器。插入器是一种迭代器适配器,带有一个容器参数,并生成一个迭代器,用于在指定容器中插入元素。三种插入器如下:
back_inserter,创建使用 push_back 实现插入的迭代器。
front_inserter,使用 push_front 实现插入。不适用于vector
inserter,使用 insert 实现插入操作。除了所关联的容器外,inserter 还带有第二个参数是迭代器,表示从该迭代器前插入。
三目运算符的第二个和第三个运算表达式必须为同一类型:
#include <list> #include <iostream> #include <algorithm> int main() { std::list<int> lt = {1,2,3,4}; auto it = std::find_if(lt.begin(),lt.end(),[&](int ele){ return ele == 3; }); it != lt.end() ? lt.erase(it) : it; //奇怪的 it,只为保持跟前面是同一类型 std::for_each(lt.begin(),lt.end(),[&](int ele){ std::cout<<ele<<std::endl; }); }
34、11.3节~11.5节待续。