前言
學習C++標準演算法時,我們知道帶有_if的演算法都帶有一個謂詞函式。此前我們直接使用函式名充當謂詞函式,這樣做有很大的侷限性( 下文會做分析 )。為此本文將介紹C++中一個新的技術:函式物件。用它來充當謂詞函式就能解決這個問題。
什麼是函式物件?
函式物件就是過載了函式呼叫操作符的物件。我們知道,函式呼叫規則已經非常固定了,那麼為何要過載函式呼叫操作符呢?因為過載後,這個函式呼叫操作符將不再用作一般意義的函式呼叫功能。不能呼叫各種函式的物件那還叫物件嗎?從語義上來講,它不符合物件的設定原則" 對現實事物的虛擬 ",因此我們叫它函式物件以區別於其他物件。那這樣的物件有什麼用?這個問題稍後討論,下面來看一個簡單的用作求絕對值的函式物件實現:
1 #include <iostream> 2 #include <string> 3 4 using namespace std; 5 6 /* 7 * 1. 函式物件的作用是虛擬函式,因此對它的命名也按照函式命名規範。 8 * 2. 過載了函式呼叫操作符,建構函式也不能用了。當然函式物件只需要()這一個" 函式 ",無需定義其他任何 9 * 函式,包括建構函式。 10 */ 11 class absInt { 12 public: 13 int operator() (int val) { 14 return val < 0? -val : val; 15 } 16 }; 17 18 19 int main() 20 { 21 absInt absObj; 22 int val1 = -1; 23 int val2 = 1; 24 25 /* 26 * absObj本質是物件,但它虛擬成了一個返回絕對值的函式。 27 */ 28 cout << absObj(val1) << endl; 29 cout << absObj(val2) << endl; 30 31 return 0; 32 }
執行結果:
仔細閱讀程式碼便能發現,該程式將物件虛擬化為一個函式來用。
為何要定義函式物件?
要物件就用物件,函式就用函式,為何費那麼大勁把物件整成函式?相信有人會這麼問。別急,我們先來回顧一個使用標準演算法的例子,這個程式碼返回容器中長度超過6的單詞:
1 #include <iostream> 2 #include <algorithm> 3 #include <vector> 4 #include <string> 5 6 using namespace std; 7 8 bool GT6 (const string &s) { 9 return s.size() >= 6; 10 } 11 12 int main() 13 { 14 /* 15 * 構建測試容器 16 */ 17 vector<string> words; 18 words.push_back("China"); 19 words.push_back("America"); 20 words.push_back("England"); 21 words.push_back("Japan"); 22 23 // 獲取容器中長度大於6的字串的個數 24 vector<string> :: size_type wc = count_if(words.begin(), words.end(), GT6); 25 26 // 列印結果 27 cout << wc << endl; 28 29 return 0; 30 }
這段程式碼沒有問題,正常地返回了結果:
但是,如果我改變注意了,又想獲得長度大於5的字串,那麼我該怎麼做?修改函式呼叫操作符過載函式是可行的辦法,但是當函式複雜起來的時候,這會是一個很麻煩的工作。好在改用函式物件充當謂詞函式後就能解決這個問題了。
使用函式物件的改進版本
1 #include <iostream> 2 #include <algorithm> 3 #include <vector> 4 #include <string> 5 6 using namespace std; 7 8 class GT_cls { 9 public: 10 GT_cls (string::size_type val=0) : bound(val) {}; 11 bool operator() (const string &s) { 12 return s.size() >= bound; 13 } 14 15 private: 16 string::size_type bound; 17 }; 18 19 int main() 20 { 21 /* 22 * 構建測試容器 23 */ 24 vector<string> words; 25 words.push_back("China"); 26 words.push_back("America"); 27 words.push_back("England"); 28 words.push_back("Japan"); 29 30 /* 31 * 獲取容器中長度大於6的字串的個數 32 * 如要取長度大於5的,只要將下面語句中的6改成5即可,不用修改過載函式。 33 */ 34 vector<string> :: size_type wc = count_if(words.begin(), words.end(), GT_cls(5)); 35 36 // 列印結果 37 cout << wc << endl; 38 39 return 0; 40 }
執行結果:
如果要找長度大於8的單詞個數?同理,將函式物件引數改成8即可。
說明
請細細體會上面的程式碼,分析其執行過程。