上次CSDN密码泄露后,修改了一个复杂的密码,又一直是自动登录,今天再登录时提示需要密码了,可是已经忘记了。使用邮箱找回来时,用了yahoo、qq邮箱都找回一个以前使用的CSDN账号,忽然间发现自己还有三个CSDN账号,怎么都忘记了呢。后来使用用户名把密码找回了,原来我一直使用的这个账号是用Google邮箱注册的,说多了。
今天学习的是boost下的function,书上是这样描述的。function是一个函数对象的“容器”,概念上像是C/C++中函数指针类型的泛华,是一种“智能函数指针”。它以对象的形式封装了原始的函数指针或函数对象,能够容纳任意符合函数签名的可调用对象。因此,它可以被用于回调机制,暂时保管函数或函数对象,在之后需要的时机在调用,使回调机制拥有更多弹性。
说道回调,以前在没有使用虚函数,即没有学习boost的function时,我认识的回调就是C语言中的typedef 返回类型 (*pointer)(参数列表);当时也一直在想,C++不是面向对象吗?怎么还一直存在typedef 定义的这种回调函数,就没有一种替代方法吗?现在是否一切都不是不可替换的了。
function可以配合bind使用,存储bind表达式的结果,使bind可以被多次调用,因为我们知道bind返回一个函数对象嘛?而且我们也可以尝尝看到他们是经常被搭配在起使用的。
那如何使用function,和bind差不多,function位于命名空间boost,为了使用function组件,需要包含头文件<boost/function.hpp>,即:
#include<boost/function.hpp>
using namespace boost;
现在主要是考虑如何使用function,因此其如何实现我们基本不去关心,直接看如何使用就OK了,function是一个模板类,因此在使用的时候需要指定函数原型如何,例如:
function<int()> func;表示func可以接受 返回类型为Int,函数无参数的函数的地址。这样真是一目了然啊。但书上也说了,有些老的编译器并不支持这种写法,要写成下面这样:
funcion<int> func,如果有一个参数就是funcion<int,int> func,这两个Int中,前面一个表示返回值,第二个表示参数,太不直观了,严重坚持第一种写法,让老编译器见鬼去吧!
function的构造函数可以接受任意符合模板中声明的函数类型的可调用对象,如函数指针和函数对象,也可以是另一个function对象的引用,之后在内部存储一份它的拷贝。无参或空指针将构造一个空function对象,不持有任何可调用物,调用空的function对象将抛出bad_function_call异常,因此在使用function前最好检查一下它的有效性。可以用empty()测试function是否为空,或者用重载操作符operator!=来测试。function对象也可以再bool上下文中直接测试它是否为空,它是类型安全的。
function的其他成员函数如clear()可以直接将function对象置空,它与使用operator=的效果一样。function模板成员函数target可以返回function对象内部持有的可调用物Functor的指针,如果function为空则返回NULL。而contain()函数可以检测function是否持有一个Functor对象。最后,function提供了operator(),它把传入参数转交给内部保存的可调用物,完成真正的函数调用。
function重载了operator==和operator!=,可以用来与被包装的函数或函数对象进行比较。如果存储的是函数指针,那么比较相当于
function.target<Functor>() == func_pointer
例如:
function<int(int,int)> func(f);
assert(func == f);如果function存储的是函数对象,那么要求函数对象必须重载operator==。
而两个function对象不能使用==和!=直接比较,这是特意的。因为function存在到bool的隐式转换,function定义了两个function对象的operator==但没有实现,企图比较两个function对象会导致编译错误,此处要问,为什么不提供比较呢,我目前还没有想到提供两个比较的意义何在,因此应该是没有这个必要吧。
在function中可以使用ref、cref库来降低function参数的拷贝工作。function不要求ref库提供operator(),因为它能够自动识别包装类referrence_wrapper<T>,并调用get()方法获得被包装的对象,见代码片段。
将function用于回调,可以说是一个强大的功能之处。在书上还列举了使用对象函数作为回调函数,用来实现带状态的回调。另外由于bind函数存在,甚至在我们使用回调函数时把不符合function要求的函数签名的函数通过bind函数来进行转换(主要是参数个数不一致的情况),而不需要改动回调接口。学习代码如下:
#include <boost/function.hpp>
#include <boost/bind.hpp>
using namespace boost;
int f(int a, int b)
{
return a + b;
}
struct demo_class
{
int add(int a,int b)
{
return a + b;
}
int operator()(int x)const
{
return x * x;
}
private:
typedef function<void(int)> func_t;//function类型定义
func_t func;
int n;//内部成员变量
public:
demo_class(){};
demo_class(int i):n(i){};
template<typename CallBack>
void accept(CallBack f)
{
func = f;
}
void run()
{
func(n);
}
};
void call_back_func(int i)
{
cout<<"call back func:";
cout<<i * 2<<endl;
}
int Test_Function()
{
function<int(int,int)> func;//无参构造function对象
assert(!func);//此时不持有任何对象
func = f;//func存储了f对象
if (func)//可以转换为bool值
{
cout<<func(10,20)<<endl;//调用function的operator()函数,并通过其将参数传给其持有的对象f
}
func = 0;//将function对象清空,相当于clear函数
assert(func.empty());//使用empty()函数判断是否为空
function<int(demo_class&,int,int)> func1;//用来存储成员函数时可以直接在function声明的函数签名中指定类的类型,然后用bind绑定成员函数,此处成员函数的返回类型没有指定啊?
func1 = bind(&demo_class::add,_1,_2,_3);//需要绑定三个参数,一个是对象,另一个函数参数
demo_class sc;
cout<<func1(sc,20,20)<<endl;//调用类的成员函数
//下面的方法直观点,保持和普通函数调用方法一致
function<int(int,int)> func2;//无参构造function对象
func2 = bind(&demo_class::add,&sc,_1,_2);//比普通函数多了一个类对象地址
cout<<func2(30,20)<<endl;//调用成员函数
function<int(int)> func3;
func3 = cref(sc);//使用cref包装常对象的引用,参看demo_class::operator(int x)const
cout<<func3(10)<<endl;
//用于回调时
demo_class dc(20);
dc.accept(call_back_func);//接受回调函数
dc.run();
return 0;
}