zoukankan      html  css  js  c++  java
  • 《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 <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

    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节待续。

  • 相关阅读:
    软件工程第二次作业
    软件工程第1次作业
    软件工程第0次作业
    第4次作业-案例分析
    第3次作业-四则运算
    第2次作业-效能分析
    第1次作业-词频统计
    第0次作业
    关于 石墨文档客户端 的案例分析
    结对编程
  • 原文地址:https://www.cnblogs.com/tianyajuanke/p/2637925.html
Copyright © 2011-2022 走看看