探祕C++仿函數

最近喵哥遇到一個問題:如何在不借助額外空間(新建vector等)來實現map自己的想法(不只是表面的升序、降序)排序(sort只適用於順序容器,map並不可以使用)。

如果忽略“不借助額外空間這個要求”,完全可以用一個vector來實現:

#include <iostream>
#include <map>
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
#include <list>

bool secSort(pair<string, int> const x, pair<string, int> const y) {
	return x.second > y.second;
}

int main()
{

	map<string, int> name_score_map;
	name_score_map["LiMin"] = 90;
	name_score_map["ZiLinMi"] = 79;
	name_score_map["BoB"] = 92;
	name_score_map.insert(make_pair("Bing", 99));
	name_score_map.insert(make_pair("Albert", 86));
	//藉助vector實現map按照second排序
	vector<pair<string, int>> vecm(name_score_map.begin(), name_score_map.end());
	sort(vecm.begin(), vecm.end(), secSort());
    return 0;
}

 


什麼是仿函數?

仿函數(functors)在C++標準中採用的名稱是函數對象(function objects)。仿函數主要用於STL中的算法中,雖然函數指針雖然也可以作爲算法的參數,但是函數指針不能滿足STL對抽象性的要求也不能滿足軟件積木的要求–函數指針無法和STL其他組件搭配,產生更靈活變化。仿函數本質就是類重載了一個operator(),創建一個行爲類似函數的對象。

使用仿函數必須包含:<functional>頭文件。先看看STL的一個仿函數,less<_Ty>

template<class _Ty = void>
    struct less
    {    // functor for operator<
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty first_argument_type;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef _Ty second_argument_type;
    _CXX17_DEPRECATE_ADAPTOR_TYPEDEFS typedef bool result_type;

    constexpr bool operator()(const _Ty& _Left, const _Ty& _Right) const
        {    // apply operator< to operands
        return (_Left < _Right);
        }
    };

乍一看,挺麻煩的,其實只要抓住幾個要點就可以自定義仿函數了:

  1. 仿函數的類必須重載operator()運算符;
  2. 重載operator()運算符時必須在函數聲明後面const,即是一個常量成員函數,這是因爲定義的類要跟binder2nd 一致,binder2nd 的 operator() 後面有個 const;

實例

#include <iostream>
#include <map>
#include <algorithm>
#include <functional>
#include <string>
#include <vector>
#include <list>

class secSort {
public:
	bool operator() (string x, string y)const {
		return x.length() > y.length();
	}
};

int main()
{

	map<string, int, secSort> name_score_map;
	name_score_map["LiMin"] = 90;
	name_score_map["ZiLinMi"] = 79;
	name_score_map["BoB"] = 92;
	name_score_map.insert(make_pair("Bing", 99));
	name_score_map.insert(make_pair("Albert", 86));
	return 0;
}

上面代碼中的secSort就是一個仿函數,在定義仿函數類的時候可以不表明繼承了binary_function與unary_function,其實在編譯的時候可以自動識別出,一個現象就是:如果重載operator()時不是常量成員函數,那麼就會報錯。

上述仿函數實現的是:按照key(string類型)的長度大小排序。如果要實現按照value排序,可以直接在secSort類中把重載函數的參數改爲int嗎?這是絕對不可以的,因爲map默認是按照key值排序的。

事實上,STL中pair的排序方式決定了map的排序。

template<class _T1, class _T2>  
  inline bool  
  operator<(const pair<_T1, _T2>& __x, const pair<_T1, _T2>& __y)  
  { return __x.first < __y.first  
           || (!(__y.first < __x.first) && __x.second < __y.second); } 

STL源碼中pair已經重載了<號,所以不可以在定義map的時候按照value排序,但是可以參照第一段代碼用vector實現按照value排序。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章