《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;
}

只有vectordeque类型迭代器支持的操作:

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即可。如果要引用该类型,则通过referenceconst_reference类型实现。在程序员编写自己的泛型程序(第十六章)时,这些元素相关类型的定义非常有用。

容器的beginend操作:

c.begin()        //返回一个迭代器,它指向容器 c 的第一个元素
c.end()           //返回一个迭代器,它指向容器 c 的最后一个元素的下一位置
c.rbegin()       //返回一个逆序迭代器,它指向容器 c 的最后一个元素
c.rend()          //返回一个逆序迭代器,它指向容器 c 的第一个元素前面的位置

上述每个操作都有两个不同版本:取决于容器是否为const。如果容器不是const,则这些操作返回iteratorreverse_iterator类型。如果容器是const,则其返回类型是const_iteratorconst_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

任何insertpush操作都可能导致迭代器失效。当编写循环将元素插入到vectordeque容器中时,程序必须确保迭代器在每次循环后都得到更新。

不要存储end操作返回的迭代器。添加或删除dequevector容器内的元素都会导致存储的迭代器失效。

容器类型和元素类型相同的容器支持用关系操作符进行比较,规则如下:

如果两个容器具有相同的长度而且所有元素都相等,那么这两个容器就相等;否则,它们就不相等。

如果两个容器的长度不相同,但较短的容器中所有元素都等于较长容器中对应的元素,则称较短的容器小于另一个容器。

如果两个容器都不是对文的初始子序列,则它们的比较结果取决于所比较的第一个不相等的元素。

但如果元素类型没有定义关系运算,则此类容器也不能进行关系运算。

顺序容器的大小操作:

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操作可能会使迭代器失效。在vectordeque容器上做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容器预留了这些额外的存储区,用于存放新添加的元素。于是,不必为每个新元素重新分配容器。所分配的额外内存容量的确切数目因库的实现不同而不同。比起每添加一个新元素就必须重新分配一次容器,这个分配策略带来显著的效率。事实上,其性能非常好,因此在实际应用中,比起listdeque容器,vector的增长效率通常会更高。

那么,vector 预留的空间是多大呢?可以通过其成员函数 capacity() 来查看。那么,如果不满意这个预留大小怎么办呢?可以通过其成员函数 reserve(int i) 来设置。

默认情况下,当 vector 增长达到预留空间大小后,分配新的存储空间时,会以加倍当前容量的分配策略实现重新分配。

标准库提供了三种顺序容器适配器:queuepriority_queuestack

使用适配器时,必须包含相关的头文件:

#include <stack>    // stack adaptor
#include <queue>    // both queue and priority_queue adaptors

所有适配器都定义了两个构造函数:默认构造函数用于创建空对象,而带一个容器参数的构造函数将参数容器的副本作为其基础值。

stack<int> stk1();          

deque<int> deq(10,3);
stack<int> stk2(deq);

默认的stackqueue都基于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适配器要求提供随机访问功能,因此可建立在vectordeque容器上,但不能建立在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

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 类型。

map 的键类型为 key_type ,值类型为 mapped_type 。

如:cout<<typeid(map::mapped_type).name()<<endl;

另外 map 的构造函数只有那三种容器通用的:map m;map m(m2);map 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

multimapmultiset所支持的操作分别与mapset的操作相同,只有一个例外:multimap不支持下标运算。不能对multimap对象使用下标操作,因为在这类容器中,某个键可能对应多个值。而因此特性,一些方法上会有一些小区别:

insert 总是会成功的。

erase 会删除对应键的所有元素,并返回删除元素的个数。而带有一个或一对迭代器参数的版本只删除指定的元素,并返回void类型。

关联容器mapset的元素是按顺序存储的,而multimapmultset也一样。因此,在multimapmultiset容器中,如果某个键对应多个实例,则这些实例在容器中将相邻存放。

查找某个键对应的值时,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)

上述操作适用于所有的关联容器,也可用于普通的mapset容器,但更常用于multimapmultiset

在同一个键上调用lower_boundupper_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类型,由rbeginrend成员函数返回。

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节待续。
原文链接: https://www.cnblogs.com/tianyajuanke/archive/2012/08/14/2637925.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/59211

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年2月9日 上午9:03
下一篇 2023年2月9日 上午9:03

相关推荐