泛化仿函数用法及参数绑定的问题
来源:互联网 发布:ui给程序员的规范图 编辑:程序博客网 时间:2024/06/08 03:07
《C++设计新思维》一书里的泛化仿函数从Command模式讲起。Command模式主要用来降低系统中命令的调用者和执行者间的依存性。设计模式的书里面一般都采用多态的机制,调用者持有Command对象的基类接口,在此处我们称为Command接口,Command接口不知道自己将被用于执行什么命令,一般只包含一个触发命令执行的虚函数,假设名为Excute。各种不同的实际执行命令的Command对象从则Command接口派生,并重写Excute虚函数。这样调用者通过Command接口来触发命令执行时,因为虚函数机制的关系,实际上调用的都是从Command接口派生的Command对象的Excute函数。在这样的设计中,调用者和各种实际的命令对象互不相见,只持有一个Command接口。在C风格的设计中,一般用回调预先保存的函数指针来实现Command模式。
泛化仿函数可以说是一种回调,但它不但可以保存函数指针,还可以处理实现了operator()的C++对象,C++对象的成员函数。具体实现方法书里面已描述得非常详细,就不再多啰嗦,在此主要说一下项目中运用泛化仿函数的一些心得。基本如下用法:
Functor<RETURN_TYPE,PARAM_TYPE_LIST> cmd1(...);
Functor有两个泛型参数,第一个为函数返回值,第二个为函数的参数列表。构造函数可接受仿函数、类成员函数以及一般函数指针。下面示例代码演示了其用法:
#include <iostream>
#include <string>
using namespace std;
using namespace Loki;
struct TestFunctor
...{
int operator()(string str)
...{
cout << str << endl;
return 0;
}
};
struct TestFunctor2
...{
int output(int i)
...{
cout << i << endl;
return 2;
}
};
void TestFunction(int i,int j)
...{
cout << i << "," << j << endl;
}
Functor<int,NullType> BindCmd1()
...{
TestFunctor f;
Functor<int,LOKI_TYPELIST_1(string) > cmd1( f );
Functor<int,NullType> bcmd1 = BindFirst( cmd1, "another bind cmd1" );
return bcmd1;
}
int _tmain(int argc, _TCHAR* argv[])
...{
//泛化仿函数基本用法
//Functor<RETURN_TYPE, PARAM_TYPELIST> cmd(...);
TestFunctor f;
TestFunctor2 f2;
//调用operator()仿函数----------------------------
Functor<int,LOKI_TYPELIST_1(string) > cmd1( f );
cmd1( "1" );
//end of 调用operator()仿函数----------------------
//调用类成员函数----------------------------------
Functor<int,LOKI_TYPELIST_1(int) > cmd2( &f2, &TestFunctor2::output );
cmd2( 2 );
//end of调用类成员函数------------------------------
//调用一般函数指针---------------------------------
Functor<void,LOKI_TYPELIST_2(int,int) > cmd3( TestFunction );
cmd3( 3,4 );
//end of调用一般函数指针---------------------------
//预先绑定命令的参数的调用1
Functor<int,NullType> bcmd1 = BindFirst( cmd1, "bind cmd1" );
bcmd1();
//end of 预先绑定命令的参数的调用1
//预先绑定命令的参数的调用2
Functor<int,NullType> bcmd1_1 = BindCmd1();
bcmd1_1();
//end of 预先绑定命令的参数的调用2
return 0;
}
用法很简单,上面的几个用法都只有两行,第一行定义泛化仿函数,第二行执行仿函数。实际项目应用中定义和执行一般都各在不同的地方,如Command模式一样,即它们在时间和空间上是分离的。
如果您使用的是LOKI0.1.5的库,在“预先绑定命令的参数的调用2”所演示的用法中会出现运行时错误,这是我在项目过程中碰到的,经过分析LOKI中Functor实现的代码,终于找到了原因。项目中实际的应用情形当然不是象演示代码中所表现的如此明显,它可能在我们没有意识到的情况下出现,示例代码只说明了在什么情况下运用才会出错。正如代码所示,当调用绑定了参数的仿函数时,如果已经离开了所绑定参数的作用域则会出错。而其罪魁祸首在于对绑定的参数作了优化。
《C++设计新思维》P123中讲到为避免函数转发的成本对函数参数作了优化,如果参数为非基本类型(非内置类型,如自定义的struct,class),则将参数类型更改为该参数的引用类型。如示例中的int operator()(string str)被优化后str参数变成string &str。我们都知道,当一个引用离开其所引用对象的作用域后,它就会变成一个dead reference,使用了dead reference,不可避免地结局就是运行时错误。或许作者不打算支持这种情形下使用参数功能,或许作者没想到我们会这么使用绑定,但至少说明了一点,过多地优化未必是件好事。
既然知道了为什么会出错,那就容易解决问题了。绑定的原理是将参数保存起来,在调用的时候取出预先保存的参数传递给要调用的函数。如果保存的类型是值类型,那不管是否离开原参数的作用域都不会出错。现在我们来找实现参数绑定的类定义。在Functor.h中找到class BinderFirst,该类中有一个 类型定义如下:
typedef typename Private::BinderFirstBoundTypeStorage<
typename Private::BinderFirstTraits<OriginalFunctor>
::OriginalParm1>::RefOrValue
BoundTypeStorage;
BoundTypeStorage即保存所绑定参数的类型定义,观其定义可以知道该类型也是做了优化,非基本类型都变成了引用。现在我们来做一点小改动,使用参数原来的类型来保存参数,修改后的定义如下:
typedef typename Private::BinderFirstTraits<OriginalFunctor>
::OriginalParm1
BoundTypeStorage;
修改完毕后,重新编译运行,一切OK了。
其实上述改动并无法确保无论使用者怎么绑定参数都没有问题,因为它只是使用参数的原始类型来保存参数,假如参数本身是个引用类型或指针类型,那在离开了所引用对象的作用域或者指针内存已被释放的情况下调用仿函数还是会出错的。引用和指针所指的内存资源的管理一向就是个难题,如果要做得十全十美,必然要付出其它的代价。况且并不是所有的用户都需要有这种功能。将这些问题留给程序员可能会是个更好的做法。我们只要了解了仿函数的特性和应用上的局限,在应用中采取适当的方法来避开这些局限就行了。
LOKI0.1.5下载地址:http://sourceforge.net/forum/forum.php?forum_id=583500
- 泛化仿函数用法及参数绑定的问题
- 泛化仿函数(functors)
- Loki泛化仿函数
- 第五章(泛化仿函数)
- 模板之泛化仿函数(一)
- 模板之泛化仿函数(二)
- springmvc 常用的绑定参数用法
- Animation动画效果中构造函数的用法及参数
- 基于Qt的函数对象(泛化仿函数)设计,可在线程中运行并通过信号返回QVariant
- C++里仿函数的用法
- 泛化物品的背包问题
- 绑定一个带参数的函数
- javascript动态绑定带参数的函数
- jQuery循环绑定不同参数的函数
- C++中仿函数/函数对象,函数指针的用法
- C++ template的一些高级用法(元编码,可变参数,仿函数,using使用方法,. C++ 智能指针)
- js关于循环中onclick绑定带参数的函数问题
- SpringMVC绑定基本类型参数的问题
- javascript软肋4
- WinHex 13.8
- .Net里一个用于驱动摄像头的类(vb.net)
- Google Maps Hacks
- VTC - Microsoft Windows Vista - Tutorials
- 泛化仿函数用法及参数绑定的问题
- The first
- 若使用基类指针delete派生类对象,需将基类和派生类的析构函数声明为virtual
- Page_Load(null,null);dropDownList_SelectedIndexChanged(null,null);
- erwin unable to find all oracle library functions 问题解决
- 回家
- IM项目-文件服务器的冲突
- vb冒泡排序函数
- Win32 二进制资源格式