C++之函式物件(Function Object) & for_each
函式物件
對於一些用到函式作為引數的c++STL演算法(如下面的for_each演算法函式),函式的傳遞當然可以用泛化的函式指標來進行,但是c++STL常使用的是函式物件,目的在於更簡潔、不依賴於當前計算機硬體體系的方式來表達演算法。
一個函式物件,即一個過載了括號操作符“()”的物件。當用該物件呼叫此操作符時,其表現形式如同普通函式呼叫一般,因此取名叫函式物件。可以參考如下連結
在c++STL中,定義了若干函式物件,包括算數運算(plus,minus,multiplies,divides,modulus和negate),用於比較(equal_to,not_equal_to,less,greater,less_equal和greater_equal)以及邏輯運算
for_each
如下,for_each函式的實現程式碼,內部使用了一個for迴圈,逐一遍歷迭代器[first,last)【左閉右開】,然後呼叫f進行操作。可見,for_each函式並沒有修改任何元素資料,只有執行的【物件】f會改動資料(所以不是嚴格意義的非變易函式)
template<class InputIter,class Function>
Function for_each(InputIter first, InputIter last, Function f)
{
for (; first != last; ++first)
f(*first); //呼叫f物件的函式operator(),對每個元素進行處理
return f; //返回函式物件
}
需要注意的是
(1)f是物件!物件!物件!不是函式,對資料進行處理的是f的過載後的operator(),所以返回值是在物件f成員變數基礎上運算得到的;
(2)這裡函式採用值傳遞(而不是引用),所以該函式將物件f返回,用以將處理後的資料儲存下來(實際是物件f發生了變化,將f返回即可讀取處理後的資料),若函式無返回值,則形參f不會發生變化,後續有程式碼為例。
程式碼
class Average { public: Average():count(0), sum(0){ } void operator()(double num) //每次呼叫這個仿函式就相當於往裡面添加了一個數 { count++; sum += num; cout<<3*num<<endl; //為了便於除錯觀察,特將各數值輸出 } double GetAverage(){ return sum / count; } //最後由這個方法得到當前的平均值 private: int count; double sum; }; template < class T ,class F> F For_each(T begin, T end, F functor) { for (T it = begin; it != end; it++) functor(*it); return functor; } int main() { vector<int> arr = { 1, 2, 3, 4, 5 }; Average result = For_each(arr.begin(), arr.end(), Average()); //這裡的Average()相當於一個臨時物件 cout << result.GetAverage() << endl; return 0; }
期初這句話沒看懂,一直以為Average()是呼叫過載()函式,【就是前面的注意(1)f是物件,不是函式】
Average result = For_each(arr.begin(), arr.end(), Average()); //這裡的Average()相當於一個臨時物件
導致一直理解為返回的functor一直沒變
return functor;
後來才理解,這裡Average()相當於建構函式,建立了一個預設引數物件,呼叫該物件的過載函式,依次對arr資料處理,最後返回改變後的物件(相當於藉助arr各個資料,改變物件,最後返回)
為了驗證上面說法,特將上述程式碼主函式部分修改如下
int main()
{
vector<int> arr = { 1, 2, 3, 4, 5 };
Average result,r1;
result = For_each(arr.begin(), arr.end(),r1);
double ave = result.GetAverage();
return 0;
}
可以發現結果
r1作為形參傳遞,最後結果r1沒變,而運算結果以物件形式返回給了result,發生了變化
再在類Average中新增建構函式
Average(int a, int b) { count = a; sum = b; }
並將主函式改成
int main()
{
vector<int> arr = { 1, 2, 3, 4, 5 };
Average result,r1(0,5);
result = For_each(arr.begin(), arr.end(),r1);
double ave = result.GetAverage();
return 0;
}
發現結果
result結果是在r1基礎上,以arr資料進行r1的過載函式【operator()】處理
綜合上述結果,可以驗證前述結論
本人新手,理解比較淺顯,望大佬多多指正!