1. 程式人生 > >C++之函式物件(Function Object) & for_each

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)以及邏輯運算

(logical_and,logical_or,logical_not)等

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()】處理

  綜合上述結果,可以驗證前述結論

  本人新手,理解比較淺顯,望大佬多多指正!