前两天做项目时用STL的排序时犯了一个低级错误,错误产生的原因是这样的:
原先编码有一vector存放一组类,其中类简化定义如下:
class UserInfo {
public: int m_iIndex;
int m_iInfo; }; vector<UserInfo*> UserInfoList;
这个vector需要排序处理(此处不考虑排序性能相关选择),于是调用STL的sort()排序。STL默认调用“<”进行比较,因此要给UserInfo类添加操作符"<"的重载:
class UserInfo {
public: int m_iIndex; int m_iInfo; bool operator<(const UserInfo& _rhs) const { if(m_iIndex < _rhs.m_iIndex) return true; else return false; } };
写好操作符重载就可以直接进行排序了?
sort(UserInfoList.begin(),UserInfoList.end()); //Warning:此时以指针地址排序!
在运行时立马发现犯了一个低级错误,上面的sort语句是根据vector中的内容,即指针地址进行排序的!
为了让sort()以我们期望的方式排序,应该写成以下这样子
sort(UserInfoList.begin(),UserInfoList.end(), SortUserInfoMethod() );
这第三个参数:仿函数--SortUserInfoMethod()就是今天的主角了。
仿函数本质是一个类,但是有一个重载了”()“操作符的成员函数,SortUserInfoMethod(),此处作为sort排序的准则使用(sort调用了SortUserInfoMethod的操作符“()”这个函数)。
SortUserInfoMethod仿函数的定义:
class SortUserInfoMethod { public: bool operator()(const UserInfo* _pInfo1,const UserInfo* _pInfo2 )const { return _pInfo1->index < _pInfo2->index; } };
仿函数有一个好处是能在内部记录状态。如上的例子中,如果想查看sort排序时每次的动作,只需改写SortUserInfoMethod如下(注意此处例子只为说明内部状态的改变,开发编写时不推荐在仿函数判断式内改变自身状态,operator()应为const):
class SortUserInfoMethod { public: SortUserInfoMethod():iCount(0){} bool operator()(const UserInfo* _pInfo1,const UserInfo* _pInfo2 ) { cout<<iCount++<<endl; return _pInfo1->index < _pInfo2->index; } private: int iCount; };
此时每次排序比较,都会让iCount自增一次并打印。
值得注意的是,sort函数内部会为SortUserInfoMethod创建一个该类的实例,这个状态变化只在sort函数内部进行!换言之,sort()再调用一次SortUserInfoMethod(),iCount仍然是从初始化值开始计数的!
如果要保存仿函数内部状态,某些情况下也可以实现,包括template参数改成传址,for_each()会返回其仿函数等等,不过这种方法似乎过于技巧化····(实例见Nicolai的C++标准程序库的仿函数一节)。