《STL标准程序库》笔记5

来源:互联网 发布:淘宝秒杀专区 编辑:程序博客网 时间:2024/05/14 16:51

自定义泛型函数(User-Defined Generic Functions

STL乃是一个可扩展的框架(framework)。你可以自定义函数和演算法。

为了在这些操作之中定义有效的迭代器,你必须使用容器提供的类型,每一种容器都提供了内部的类型定义:

template <class T>

inline void PRINT_ELEMENTS(const T& coll, const char* optcstr="" )

{

typename T::const_iterator pos;

std::cout << optcstr;

for( pos=coll.begin(); pos!=coll.end(); ++pos ) {

std::cout << *pos << ' ';

}

std::cout << std::endl;

}

typename T::const_iterator pos;

其中pos被定义为传入容器类型内的迭代iq类型,关键字typename不可或缺,用以表明const_iterator是类型T所定义的一个类型。

以函数做为演算法的参数

一些演算法可以接收用户定义的辅助性函数,由此提供其弹性和能力:

#include <iostream>

#include <vector>

#include <algorithm>

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;

}

for_each()函数针对[coll.begin(),coll.end())区间内的每个元素调用print()

演算法以数种态度来面对这些辅助函数:

#include <iostream>

#include <vector>

#include <set>

#include <algorithm>

#include "print.hpp"

using namespace std;

int square( int value )

{

return value*value;

}

int main()

{

set<int> coll1;

vector<int> coll2;

for(int i=1; i<=9; ++i ) {

coll1.insert(i);

}

PRINT_ELEMENTS(coll1,"initialized: ");

transform(coll1.begin(),coll1.end(),

back_inserter(coll2),square);

PRINT_ELEMENTS(coll2,"squared: ");

}

此例,square()的作用是将coll1内的每一个元素平方运算,然后转移到coll2

判断式(Predicates

判断式通常被用来指定排序准则和搜寻准则。STL要求,面对相同的值,predicates必须得出相同的结果。

#include <iostream>

#include <list>

#include <algorithm>

#include <cstdlib>

using namespace std;

bool isPrime(int number)

{

number = abs(number);

if( number == 0 || number == 1 ) return true;

int divisor;

for(divisor=number/2; number%divisor != 0; --divisor );

return divisor == 1;

}

int main()

{

list<int> coll;

for(int i=24; i<=30; ++i ) {

coll.push_back(i);

}

list<int>::iterator pos;

pos = find_if(coll.begin(),coll.end(),isPrime);

if( pos !=coll.end() )

cout << *pos << " is first prime number found" << endl;

else

cout << "no prime number found" << endl;

}

二元判断式(Binary Predicates

比较两个参数的特定属性。如果元素本身不支持operator<或如果你向使用不同的排序原则,这就派上用场。

仿函数(FunctorsFunction Object

传递给演算法的参数,并不一定是函数,可以是行为类似函数的对象,这种对象称为函数对象(function object),或称仿函数(functor)。

#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<=9; ++i ) {

coll.push_back(i);

}

for_each(coll.begin(),coll.end(),PrintInt());

cout << endl;

}

for_each()演算法大致如下:

namespace std {

template <class Iterator, class Operation>

Operation for_each(Iterator act, Iterator end, Operation op)

{

while( act != end ) {

op(*act);

++act;

}

return op;

}

}

仿函数通常比一般函数速度快。

如果你需要数个不同的固定值,而它们在编译期都已确切,你可以使用template

template <int theValue>

void add( int& elem)

{

elem += theValue;

}

void f1()

{

vector<int> coll;

for_each(coll.begin(),coll.end(),

add<10>);

}

如果使用仿函数,你就可以写出更机灵的函数。对象可以有自己的状态,可以被正确初始化。下面是一个完整的例子:

#include <iostream>

#include <list>

#include <algorithm>

#include "print.hpp"

using namespace std;

class AddValue {

private:

int theValue;

public:

AddValue(int v) : theValue(v) {}

void operator() (int& elem) const {

elem += theValue;

}

};

int main()

{

list<int> coll;

for(int i=1; i<=9; ++i) {

coll.push_back(i);

}

for_each(coll.begin(),coll.end(),

 AddValue(10));

PRINT_ELEMENTS(coll,"after adding 10: ");

for_each(coll.begin(),coll.end(),

AddValue(*coll.begin()));

PRINT_ELEMENTS(coll,"after adding first element: ");

}

预先定义的仿函数

一个典型的例子是作为排序准则的仿函数。operator<预设准则是less<>,所以,如果你定义:

set<int> coll;

会被扩展为:

set<int,less<int> > coll;

既然如此,反向排序将不是什么难事:

set<int,greater<int> > coll;

还有很多仿函数用于数值处理。下例是将群集中的全部元素都设为反相(负值):

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

其中negate<int>()根据预先定义好的template class negate生成一个仿函数,将传进来的int值设置为负。transform()演算法使用此一运算,将第一群集的所有元素处理之后转移到第二群集。

同样道理,你也可以对群集内的所有元素求平方。

transform(coll.begin(),coll.end(),coll.begin(),coll.begin(),multilies<int>());

这里使用了transform演算法的另一种形式,将两群集内的元素处理后的结果写入第三群集。

透过一些特殊的函数配接器(function adaptors),你还可以将预先定义的仿函数和其他数值组合在一起,或使用特殊的情况。

#include <iostream>

#include <set>

#include <deque>

#include <algorithm>

#include "print.hpp"

using namespace std;

int main()

{

set<int,greater<int> > coll1;

deque<int> coll2;

for(int i=1; i<=9; ++i) {

coll1.insert(i);

}

PRINT_ELEMENTS(coll1,"initialized: ");

transform( coll1.begin(),coll1.end(),

back_inserter(coll2),

bind2nd(multiplies<int>(),10));

PRINT_ELEMENTS(coll2,"transformed: ");

replace_if( coll2.begin(),coll2.end(),

bind2nd(equal_to<int>(),70),

42);

PRINT_ELEMENTS(coll2,"replaced: ");

coll2.erase(remove_if(coll2.begin(),coll2.end(),

bind2nd(less<int>(),50)),

coll2.end());

PRINT_ELEMENTS(coll2,"removed: ");

}

其中的语句:

transform( coll1.begin(),coll1.end(),

back_inserter(coll2),

bind2nd(multiplies<int>(),10));

coll1内的所有元素乘以10后插入到coll2中。这里使用配接器bind2nd,使得进行multiples<int>运算时,以源群集(source collection)的元素作为第一参数,10作为第二参数。

配接器bind2nd的工作方式如下:调用bind2nd时,bind2nd把该元素当作第一参数,把原先保存下来的那个内部数值作为第二参数,调用保留下来的那个运算式,并返回结果。

类似情况:

replace_if(coll2.begin(),coll2.end(),

bind2nd(equal_to<int>(),70),

42);

其中的运算式:

bind2nd(equal_to<int>(),70);

被用来当作一项准则,半段那些元素将被42替代。bind2nd70作为第二参数,调用二元判断式(binary predicateequal_to,从而定义出一个一元判断式(unary predicate),处理群集内的每一个元素。

此种方式的程序编写,导致函数的组合。有趣的是,所有这些仿函数通常都是定义为inline。如此一来,你一方面使用类似函数式的表示法或抽象性,一方面又能获得出色的效能。

另外某些仿函数可用来调用群集内每个元素的成员函数:

for_each(coll.begin(),coll.end(),

mem_fun_ref(&Person::save) );

仿函数mem_fun_ref用来调用它所作用的元素的某个成员函数。因此上例就是针对coll内的每个元素调用Person::save()。当然啦,唯有当这些元素的类型是Person,或Person的衍生类,以上代码才能有效运作。

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