注:本文的内容并非C++11。不知道目前是什么情况,是否能够解决这个问题。
在工作中,遇到了这样一种情况:
使用std::vector<MyClass>存储了一系列数据,用于表格展示,MyClass中的每一个字段显示为一列。表格是要求点击表头排序的。
不考虑显示组件本身就支持排序的情况,由底层使用std::sort来实现排序。根据点击的表头不同,std::sort的第三个参数选择的比较对象也不同。由于比较涉及到的业务比较复杂,可能是数值比较、字符串比较、多字段计算比较,可能有优先级,等等。
很明显这是一个多态的例子,也很适合使用工厂模式。最初的想法是使用简单工厂模式,所有比较函数对象有一个共同的基类BaseComparator,使用相同的基类指针BaseComparator*来引用。
std::sort(myVec.begin(), myVec.end(), *ComparatorFactory.getInstance.getComparator(columnEnum));
然后就报错了:
error C2893: 未能使函数模板“void std::sort(_RanIt,_RanIt,_Pr)”专用化用下列模板参数:’std::_Vector_iterator<_Ty,_Alloc>’ with [ _Ty=int,_Alloc=std::allocator ]
想了半天,才明白sort是模板函数,必须在编译期确定参数类型。第三个参数是不能使用这种多态的方式的。具体说,就是不能使用基类指针指向派生类,除非强制转换成派生类指针(编译期确定)。
还犯了一个低级错误:考虑到这些比较函数对象会在运行期间多次调用,为了避免创建对象的开支,使用单例模式在程序运行开始时就把所有可能使用的对象创建出来,也是行不通的,因为在std::sort调用时一定会发生copy。
解决办法:
用另外一个静态类Wrapper包装一下,此类内部使用指向基类的指针,同时包装类的operator()直接调用内部指针的operator()(代理)。在这个Wrapper的构造函数中传入适当参数,让Wrapper在创建时再去调用Factory获取相应的比较函数对象。这时这些比较函数对象可以使用单例模式了,因为Wrapper中只是一个指针。
评价:
这样做其实导致代码复杂性提高,可读性下降,但是避免了重复代码,也有利于维护和修改的集中处理。