很多STL算法都使用了函数对象——也叫函数符。函数符是可以以函数的方式与()结合使用的任意对象。包括函数名,函数指针,重载()的类对象。
例如,这样的一个类就是函数符
class A
{
private:
int a;
public:
A(int x = 20) : a(x) {}
int operator() (int b)
{
return a + b;
}
};
int main()
{
A temp;
cout << temp(10);
return 0;
}
运行结果
基于函数符的for_each,for_each第三个参数可接受函数指针,但是通用性不强,为了提高通用性,可以传入模板当作函数符。
用模板函数
void show(const T & t)
{
cout << t << " ";
}
int main()
{
int num[] = {5,2,1,3,6,9,8,7,20,10};
list ls(num,num+10);
//ostream_iterator out (cout," ");
for_each(ls.begin(), ls.end(),show);
return 0;
}
可以这样做,使用模板类
template
class show
{
public:
void operator()(T & t)
{
cout << t << " ";
}
};
int main()
{
int num[] = {5,2,1,3,6,9,8,7,20,10};
list ls(num,num+10);
//ostream_iterator out (cout," ");
for_each(ls.begin(), ls.end(),show());
return 0;
}
运行结果
STL定义的函数符的概念
生成器:不用参数就可以调用的函数
一元函数:使用一个参数就可以调用的函数
二元函数:使用两个参数就可以调用的函数
进一步的
谓词:返回值是bool的一元函数
二元谓词:返回值是bool的二元函数
可以把接受两个参数的模板函数变为接受一个参数的模板类( 重载() )。
例如
template
void show(const T & t,int a) {
cout << t + a << " ";
}
int main()
{
list ls = {1,2,3,4,5,6,7,8,9,};
for(auto i = ls.begin();i!=ls.end();i++){
show(*(i),10);
}
return 0;
}
变为
template
class show
{
private:
T a;
public:
show(const T & x) : a(x) {}
void operator()(const T & t)
{
cout << t + a << " ";
}
};
int main()
{
show s(10);
list ls = {1,2,3,4,5,6,7,8,9,};
for(auto i = ls.begin();i!=ls.end();i++){
s(*(i));
}
cout << endl;
//or
for_each(ls.begin(), ls.end(),show(5));
return 0;
}
运行结果
C++预定的一些函数符
例如transform()——他有两种版本
第一个版本接受四个参数,前两个参数是迭代器,表示区间,第三个参数是表示指向复制到哪里的迭代器,最后一个参数是函数符,应用于每一个元素。
例如求每个数的平方根并且复制到输出流
int main()
{
list ls = {1,2,3,4,5,6,7,8,9,};
ostream_iterator out(cout, " ");
transform(ls.begin(),ls.end(),out,sqrt);
return 0;
}
第二个版本jieshou五个参数,前两个同上,第三个表示第二个容器的起始位置,第四个表示指向复制到哪里的迭代器,第五个是二元函数符,应用于每两个元素。
例如求两个容器的每两个元素和并复制到输出流
template
T add(T & t1, T & t2)
{
return t1 + t2;
}
int main()
{
//C++预定的函数符sqrt
list ls = {1,2,3,4,5,6,7,8,9,};
list ls1 = {9,8,7,6,5,4,3,2,1};
ostream_iterator out(cout, " ");
transform(ls.begin(),ls.end(),ls1.begin(),out,add);
return 0;
}
运行结果
头文件
#include
+ plus - minus * multiplies / divides % modulus - negate == equal_to != not_equal_to > greater < less >= greater_equal <= less_equal && logical_and || logical_or ! logical_not
注意他们本质上是class,只不过重载了()才得以像函数那样。
自适应函数符和函数适配器
自适应就是在处理和分析过程中,根据处理数据的数据特征自动调整处理方法、处理顺序、处理参数、边界条件或约束条件,使其与所 处理数据 的 统计分布 特征、结构特征相适应,以取得最佳的处理效果的过程。
STL有5个相关概念
自适应生成器
自适应一元函数
自适应二元函数
自适应谓词
自适应二元谓词
使函数符成为自适应的原因是它携带了标识参数类型和返回类型的typedef成员。这些成员分别是
result_type, first_argument_type, second_argument_type。
函数符自适应性的意义在于,函数适配器对象 可以使用 函数对象,并认为存在这些typedef的成员。例如, 接受一个 自适应性函数符 参数 的函数 可以使用result_type成员来声明一个于函数的返回类型匹配的变量。
STL提供了只用这些工具的函数适配器类
假设要把容器中的所有元素 x2,可以使用二元函数multiplies, 也可以使用适配写生成一个一元函数。
二元函数
#include
#include
#include
#include
#include
using namespace std;
int main()
{
multiplies mul;
list ls = {1,2,3,4,5,6,7};
for(auto i = ls.begin();i != ls.end();i++)
{
*(i) = mul(*(i),2);
}
ostream_iterator out (cout," ");
copy(ls.begin(),ls.end(),out);
return 0;
}
这样写显着我们很捞,可以使用函数适配器(本身是一个模板类)
binder1st——可创建其一个对象,该对象可以将一个值与二元函数第一个参数关联从而该对象变为一元函数
binder2nd——同上,但将一个值与二元函数的第二个值关联
因为本质是模板类使用时注意实例化
或者是这两个函数
同上,但是是个函数,不用实例化
bind1st()——返回一个一元函数符对象
bind2nd()——返回一个一元函数符对象
于是可以这样改写代码
int main()
{
list ls = {1,2,3,4,5,6,7};
ostream_iterator out (cout," ");
binder1st< multiplies > my_mul (multiplies(),2);
transform(ls.begin(), ls.end(),out,my_mul);
return 0;
}
或者
int main()
{
list ls = {1,2,3,4,5,6,7};
ostream_iterator out (cout," ");
transform(ls.begin(), ls.end(),out,bind1st(multiplies(),2));
return 0;
}
运行结果
两种方法使用时稍微有些不同,函数的方式会更简单一些