C++ STL — 第5章 The STL(二)

  变动型算法:manipulating algorithm

    1. remove移除元素

    不改变群集中的元素数目,end()返回的还是原来的那个点,size()还是当初那个大小,元素次序改变,有些元素被删了,删掉的元素的位置由后面的元素替换。一般可以erase掉remove()返回的iterator到end()之间的元素来真正实现删除。

#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
 
using namespace std;
 
int main()
{
    list<int> coll;
    for(int i=1; i<=6; ++i)
    {
        coll.push_front(i);
        coll.push_back(i);
    }
    list<int> coll1(coll.begin(), coll.end());
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," ")); //6 5 4 3 2 1 1 2 3 4 5 6
    cout << endl;
 
    remove(coll.begin(), coll.end(), 3);
 
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," ")); //6 5 4 2 1 1 2 4 5 6 5 6
    cout << endl;
 
    copy(coll1.begin(), coll1.end(), ostream_iterator<int>(cout," ")); //6 5 4 3 2 1 1 2 3 4 5 6
    cout << endl;
 
    list<int>::iterator end = remove(coll1.begin(), coll1.end(), 3);
    cout << "number of removed elements : " << distance(end, coll1.end()) << endl; //2
    coll1.erase(end, coll1.end());
 
    copy(coll1.begin(), coll1.end(), ostream_iterator<int>(cout," ")); //6 5 4 2 1 1 2 4 5 6
    cout << endl;
    return 0;
}

 remove():返回逻辑上的新终点

    distance():返回两个迭代器之间的距离

    erase():删除迭代器之间的元素

    不直接调用容器的算法:这个设计使得算法的操作对象不一定非得是整个容器内的所有元素,可以是那些元素的子集。

    2. 与关联式容器 -- 用于它会出问题

    因为关联式容器总是根据自身某个排序准则自动排序,所以关联式容器的所有迭代器均被声明为指向常量

    删除元素:erase()函数

#include <iostream>
#include <set>
#include <iterator>
#include <algorithm>
 
using namespace std;
 
int main()
{
    set<int> coll;
    for(int i=1; i<=9; ++i)
    {
        coll.insert(i);
    }
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));//1 2 3 4 5 6 7 8 9
    cout << endl;
 
    int num = coll.erase(3);
    cout << "number of removed elements : " << num << endl; //1
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));//1 2 4 5 6 7 8 9
    cout << endl;
    return 0;
}

如果是list,使用成员函数也许是个更好的选择,效率更高:

#include <iostream>
#include <list>
#include <iterator>
#include <algorithm>
 
using namespace std;
 
int main()
{
    list<int> coll;
    for(int i=1; i<=9; ++i)
    {
        coll.push_front(i);
        coll.push_back(i);
    }
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));
    //9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9
    cout << endl;
 
    coll.remove(3);
    copy(coll.begin(), coll.end(), ostream_iterator<int>(cout," "));
    //9 8 7 6 5 4 2 1 1 2 4 5 6 7 8 9
    cout << endl;
 
    return 0;
}

使用自定义泛型函数:

//print.cpp
#include <iostream>
 
using namespace std;
 
template <class T>
inline void print_elements(const T& coll, const char* optstr="")
{
    typename T::const_iterator pos;
    cout << optstr;
    for(pos=coll.begin(); pos!=coll.end(); ++pos)
    {
        cout << *pos << " ";
    }
    cout << endl;
}
 
//testPrint.cpp
#include <iostream>
#include <list>
#include "print.cpp"
 
using namespace std;
 
int main()
{
    list<int> coll;
    for(int i=1; i<=9; ++i)
    {
        coll.push_back(i);
    }
    print_elements(coll, "list : ");
    return 0;
}
//list : 1 2 3 4 5 6 7 8 9

注意:typename不可少,表明const_iterator是性别T所定义的一个型别,而不是一个型别为T的值

以函数作为算法的参数:被算法用作参数内部调用

#include <iostream>
#include <algorithm>
#include <vector>
 
using namespace std;
 
void print(int elem)
{
    cout << elem << " ";
}
 
int main()
{
    vector<int> coll;
    for(int i=1; i<=9; ++i)
    {
        coll.push_back(i);
    }
    for_each(coll.begin(), coll.end(), print);
    cout << endl;
    return 0;
}
//1 2 3 4 5 6 7 8 9

#include <iostream>
#include <set>
#include <vector>
#include <algorithm>
#include <iterator>
#include "print.cpp"
 
using namespace std;
 
int square(int value)
{
    return value * value;
}
 
int main()
{
    set<int> c1;
    vector<int> c2;
 
    for(int i=1; i<=5; ++i)
    {
        c1.insert(i);
    }
 
    print_elements(c1, "init c1: ");//init c1: 1 2 3 4 5
 
    std::transform(c1.begin(), c1.end(), std::back_inserter(c2), square);
 
    print_elements(c2, "squared : ");//squared : 1 4 9 16 25
    return 0;
}

判断式:一种函数,返回布尔值,通常用来指定排序准则和搜索准则

#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
 
using namespace std;
 
bool isPrime(int num)
{
    if(num == 1 || num == 0)
        return false;
    if(num == 2)
        return true;
    for(int i=2; i <= sqrt(num); i++)
    {
        if(num % i == 0)
            return false;
    }
    return true;
}
 
int main()
{
    vector<int> c;
    for(int i=24; i<=30; ++i)
    {
        c.push_back(i);
    }
    vector<int>::iterator iter;
    iter = find_if(c.begin(), c.end(), isPrime);
    if(iter != c.end())
    {
        cout << *iter << " is first prime number" << endl;//29 is first prime number
    } else
    {
        cout << "no prime number" << endl;
    }
    return 0;
}

find_if函数在给定区间寻找使得被传入的isPrime判断式结果为true的第一个元素

函数对象:function object

类似函数的对象,泛型编程的抽象概念

定义:

class X {
public:
    return-value operator()(arguments) const
    {
        //...
    }
};
 
X fo;
fo(arg1, arg2);  //等价于call fo.operator()(arg1,arg2);

#include <iostream>
#include <vector>
#include <algorithm>
 
using namespace std;
 
class PrintInt
{
public:
    void operator() (int elem) const
    {
        cout << elem << " ";
    }
};
 
int main()
{
    vector<int> coll;
    for(int i=1; i<=5; i++)
    {
        coll.push_back(i);
    }
    for_each(coll.begin(), coll.end(), PrintInt());
    cout << endl;
    return 0;
}
//1 2 3 4 5

PrintInt()会产生一个临时对象

    foreach的第三个参数:如果是个一般函数,就以参数调用之,如果是个函数对象,就以参数调用函数对的operator()运算符

    比起一般函数,函数对象具有以下优点:

    1. 智能型函数,因为可以拥有成员函数和成员变量,还可以在执行期间初始化它们

    2. 每个函数对象都有自己的类型,利用template,可以将函数行为当作template参数使用

    3. 函数对象比一般函数速度快,template更多细节在编译期间就确定。

    现在有个需求,对容器里的每个元素加上一个值

    如果在编译期间就知道这个固定值,则可以这样:

void add10(int& elem)
{
    elem = elem + 10;
}
 
void f()
{
    vector<int> coll;
    for_each(coll.begin(), coll.end(), add10);
}

如果需要多个不同的值,编译期间已经确定:

template <int theValue>
void add(int& elem)
{
    elem = elem + theValue;
}
void f()
{
    vector<int> coll;
    for_each(coll.begin(), coll.end(), add<10>);
}

使用函数对象,则更灵活:

#include <iostream>
#include <list>
#include <algorithm>
#include "print.cpp"
 
using namespace std;
 
class AddValue
{
private:
    int theValue;
public:
    AddValue(int v) : theValue(v) {}
    void operator() (int& elem) const
    {
        elem = elem + theValue;
    }
};
 
int main()
{
    list<int> c;
    for(int i=1; i<=5; ++i)
    {
        c.push_back(i);
    }
 
    print_elements(c, "initialized : ");//1 2 3 4 5
 
    for_each(c.begin(), c.end(), AddValue(10));
 
    print_elements(c, "after add 10 : ");//11 12 13 14 15
 
    for_each(c.begin(), c.end(), AddValue(*c.begin()));
 
    print_elements(c, "after add first elem : ");//22 23 24 25 26
 
    AddValue add100(100);
    for_each(c.begin(), c.end(), add100);
    print_elements(c, "after add 100 : "); //122 123 124 125 126
    return 0;
}

 C++ STL预先定义的函数对象:

    1. 作为排序准则的函数对象,operator < 缺省排序准则是less<>从小到大

       set<int> c;   //其实就是set<int, less<int> >  c;

       set<int, greater<int> > c;  //从大到小

    2. 相反值:negate<int>()   

    transform(c.begin(), c.end(), c.begin(), negate<int>());

    函数配接器:将预定义的仿函数和其他数值结合在一起

#include "print.cpp"
#include <set>
#include <deque>
#include <algorithm>
#include <iostream>
 
using namespace std;
 
int main()
{
    set<int, greater<int> > c1;
    deque<int> c2;
 
    for(int i=1; i<=5; ++i)
    {
        c1.insert(i);
    }
    print_elements(c1, "set : "); //5 4 3 2 1
 
    transform(c1.begin(), c1.end(), back_inserter(c2), bind2nd(multiplies<int>(), 10));
 
    print_elements(c2, "transformed : "); //50 40 30 20 10
 
    replace_if(c2.begin(), c2.end(), bind2nd(equal_to<int>(), 30), 55);
 
    print_elements(c2, "replaced : "); //50 40 55 20 10
 
    c2.erase(remove_if(c2.begin(),c2.end(),bind2nd(less<int>(),50)), c2.end());
 
    print_elements(c2, "removed : "); //50 55
    return 0;
}

容器内元素的条件:

    1. 可透过copy构造函数进行复制。副本与原本必须相等。

    2. 透过assignment操作符完成赋值操作。

    3. 透过析构函数完成销毁动作。析构函数不能为private

    4. 对于序列式容器,元素的默认构造函数可用

    5. 对于某些操作,必须定义 == 执行相等操作

    6. 关联式容器元素必须定义排序准则,缺省下operator<,透过仿函数less<>调用

    STL容器提供的是value语意,所容纳的副本而不是对象本身,而不支持reference。

    好处:拷贝简单;reference容器出错;

    缺点:拷贝性能不好;无法在不同的容器中管理同一份对象;

    但我们可以使用指针作为容器元素,必须很谨慎使用!

    千万不要在容器内放置auto_ptrs,拷贝和赋值操作后,目标对象与原来对象不相等。

    通过引用计数智能型指针,可以实现STL容器的reference语意

    

    STL内部错误处理和异常处理:

    错误处理:

    STL的任何运用,如果违反规则,将会导致未定义行为

    迭代器务必合法且有效

    一个迭代器如果指向pass-the-end位置,并不指向任何对象

    区间必须合法 - 前后两个迭代器必须指向同一个容器;从第一个出发能够到达第二个

    如果涉及多个区间,第二区间必须拥有至少第一区间一样多的元素

    覆盖动作中目标区间必须拥有足够的元素,否则用insert iterator


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值