《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<或如果你向使用不同的排序原则,这就派上用场。
仿函数(Functors,Function 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替代。bind2nd以70作为第二参数,调用二元判断式(binary predicate)equal_to,从而定义出一个一元判断式(unary predicate),处理群集内的每一个元素。
此种方式的程序编写,导致函数的组合。有趣的是,所有这些仿函数通常都是定义为inline。如此一来,你一方面使用类似函数式的表示法或抽象性,一方面又能获得出色的效能。
另外某些仿函数可用来调用群集内每个元素的成员函数:
for_each(coll.begin(),coll.end(),
mem_fun_ref(&Person::save) );
仿函数mem_fun_ref用来调用它所作用的元素的某个成员函数。因此上例就是针对coll内的每个元素调用Person::save()。当然啦,唯有当这些元素的类型是Person,或Person的衍生类,以上代码才能有效运作。
STL内部的错误处理和异常处理
- 《STL标准程序库》笔记5
- 《STL标准程序库》笔记1
- 《STL标准程序库》笔记2
- 《STL标准程序库》笔记3
- 《STL标准程序库》笔记4
- 《C++标准程序库STL》笔记
- 《STL标准程序库》笔记6-vector
- 《STL标准程序库》笔记7-deque
- 《STL标准程序库》笔记8-List
- C++标准程序库STL
- 《C++标准程序库》学习笔记(一)初识STL
- C++标准程序库 学习笔记 第九章 STL算法
- C++ STL标准程序库初探
- C++标准程序库 学习笔记 第五章 STL(标准模板库概述)
- C++标准程序库学习笔记
- C++标准程序库笔记---迭代器
- 阅读笔记《C++标准程序库》
- C++标准程序库与STL关系
- 换项目组了
- 已发归档邮件做备份(补充)
- Net设计模式实例之建造者模式(Builder Pattern)
- 信号量与自旋锁
- 获得文件版本等信息
- 《STL标准程序库》笔记5
- [Java] 线程、线程池与工作队列
- 看了篇某博客之星写的有点感触,赞同观点记下,努力
- log4j入门教程
- border
- TOLUA++初探
- 我的浮躁
- poj1279——Art Gallery//半平面交 求面积
- a