1、容器(Containers):各种数据结构,如Vector,List,Deque,Set,Map,用来存放数据,STL容器是一种Class Template,就体积而言,这一部分很像冰山载海面的比率。
2、算法(Algorithms):各种常用算法如Sort,Search,Copy,Erase,从实现的角度来看,STL算法是一种Function Templates。
3、迭代器(Iterators):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,共有五种类型,以及其它衍生变化,从实现的角度来看,迭代器是一种将:Operators*,Operator->,Operator++,Operator--等相关操作予以重载的Class Template。所有STL容器都附带有自己专属的迭代器——是的,只有容器设计者才知道如何遍历自己的元素,原生指针(Native pointer)也是一种迭代器。
4、仿函数(Functors): 行为类似函数,可作为算法的某种策略(Policy),从实现的角度来看,仿函数是一种重载了Operator()的Class 或 Class Template。一般函数指针可视为狭义的仿函数。
5、配接器(适配器)(Adapters):一种用来修饰容器(Containers)或仿函数(Functors)或迭代器(Iterators)接口的东西,例如:STL提供的Queue和Stack,虽然看似容器,其实只能算是一种容器配接器,因为 它们的底部完全借助Deque,所有操作有底层的Deque供应。改变Functor接口者,称为Function Adapter;改变Container接口者,称为Container Adapter;改变Iterator接口者,称为Iterator Adapter。配接器的实现技术很难一言蔽之,必须逐一分析。
6、分配器(Allocators):负责空间配置与管理,从实现的角度来看,配置器是一个实现了动态空间配置、空间管理、空间释放的Class Template。
——《STL源码剖析》

函数子类(函数对象)的由来:
在STL里,我们经常需要对容器内元素进行批量操作,手写循环确实是个方法,但是,代码冗余,而且效率普遍偏低,这就需要一个批量操作方法,能够对容器内的元素进行批量操作,那么,怎么操作,操作什么,这就需要用户去定义了,你需要告诉程序,你想要什么样的操作,一般人对于这种问题的第一反应就是函数指针,确实,通过传递一个函数指针可以让程序执行特定的操作,这也确实是一个方法,但是按CPP Primer Plus上的原文来说,它有一个很大的弊端,那就是函数指针是必须指定参数以及返回类型的,而STL属于泛型编程,加入用函数指针来操作的话,是违反泛型思想的本质的,因此,这就需要一个能取代函数指针的东西了,而这个东西就是所谓的函数对象。
判别式类是指函数子类,它的operator()函数是一个判别式,也就是说,它的operator()返回true 或false .
例子:effective 41条
template
class MeetsThreshold:public std::unary_function
private:
const T threshold ;
public:
MeetsThreshold(const T& threshold);
bool operator()(const Widget&) const ;
};
struct WidgetNameCompare:public std::binary_function
bool operator()(const Widget& lhs,const Widget& rhs) const;
};
注意operator()的类型是const Widget& 但是给unary_function binary_function 的类型是Widget . 一般情况下传递给unary_function binary_function非指针类型要去掉const 和 引用。如果operator() 参数是指针类型,情况就不一样了, 二者要一样。如下
struct WidgetNameCompare:public std::binary_function<const Widget*,const Widget*,bool>{
bool operator()(const Widget* lhs,const Widget* rhs) const;
};
这样就完成了可配接函数对象
list
list
list
引入函数子类概念,如上所述,STL 泛型思想就统一了,不必非得引入函数指针而违背泛型原则,这样的STL就正统了。
STL 函数子类: less
为了更好理解上面,关于函数子类原理可以查资料了解一下。思想就是类可以保存本类指针成员(非静态),并且重载了operator(),从而随时可以用指针去调用operator();
函数子类是值传递和返回,参见effective 38 条
函数子类判别式应是“纯函数” 参见effective 39 条
ptr_fun men_fun men_fun_ref 的由来
先说函数对象配接器mem_fun mem_fun_ref :
f(x); // 语法#1:当f是一个非成员函数
x.f(); // 语法#2:当f是一个成员函数,而且x是一个对象或一个对象的引用
p->f(); // 语法#3:当f是一个成员函数,而且p是一个对象的指针
vector
for_each(vw.begin(),vw.end(), test);
但是假如 test是Widget 的成员函数,即Widget 支持自测:
class Widget{
public:
...
void test(); //自测,如果不通过,*this 标记为“失败”
...
};
for_each 在生个Widget对象vw上调用自测 Widget::test成员函数:
for_each(vw.begin(),vw.end(),&Widget::test); //#2 编译不通过
那么容器存的是Widget* 指针呢,情况会怎么样
list
for_each(lpw.begin(),lpw.end(),&Widget::test); //#3 编译不通过
看一下for_each源码
template
Function for_each(InputIterator, begin,InputIterator end,Function f)
{
while(begi != end) f(*begin++);
}
可见调用语法是#1, 所以#2 #3 不通过。
所以就有了mem_fun men_fun_ref ,能过这二个函数配接器使#2 #3 调整成 #1 。
所以上面的代码改成如下:
for_each(vw.begin(),vw.end(),mem_fun_ref(&Widget::test)); //#2 通过 正确
list
for_each(lpw.begin(),lpw.end(),mem_fun(&Widget::test)); //#3 通过 正确
STL 中凡是能接受判别式的地方,就既可以接受一个真正的函数,也可以授受一个判别式类的对象。 反之亦然。
再说下 ptr_fun 函数对接器,ptr_fun 是针对普通函数,就是#1情况,如上所述,#1是正确的方式, 这时不需要Ptr_fun, 什么时需要呢? 就是需要配合其他配接器的时候才需要用它,比如4个标准函数对接器 not1 not2 bind1st bind2nd 时,就需要加上ptr_fun(被迫), 如下面这段话的解释。
如果void test() 改成 bool test()const, mem_fun mem_fun_ref 是不是可以使成员函数替代判别式类 ? 答案是肯定的,看代码
class TestClass1{
public:
TestClass1(int d):data(d)
{}
bool test() const
{
return data>4;
}
private:
int data;
};
TestClass1 *t1 = new TestClass1(1);
TestClass1 *t2 = new TestClass1(2);
TestClass1 *t3 = new TestClass1(4);
TestClass1 *t4 = new TestClass1(5);
std::vector
lpt.push_back(t1);
lpt.push_back(t2);
lpt.push_back(t3);
lpt.push_back(t4);
std::vector
还是上面的代码改造一下,test 传入一个参数用来判断,找出大于值大于 val 的元素。
class TestClass1{
public:
TestClass1(int d):data(d)
{}
bool test(int val) const
{
return data>val;
}
private:
int data;
};
std::vectorok,到此基本介绍完了,这里留个疑问,就是上面为什么是std::bind2nd,test(int val)就只有一个参数。 关于bind1st bind2nd 另一篇细讲。
mem_fun men_fun_ref 源码分析,不错
http://blog.csdn.net/jxlczjp77/article/details/1751917
http://www.cnblogs.com/tracylee/archive/2012/11/15/2772410.html