C++11新特性之八——函數對象function


詳細請看《C++ Primer plus》(第六版中文版)

http://www.cnblogs.com/lvpengms/archive/2011/02/21/1960078.html

備注:

函數對象
盡管函數指針被廣泛用於實現函數回調,但C++還提供了一個重要的實現回調函數的方法,那就是函數對象。函數對象(也稱“函數符”)是重載了“()”操作符的普通類對象。因此從語法上講,函數對象與普通的函數行為類似。
用函數對象代替函數指針有幾個優點:

  • 首先,因為對象可以在內部修改而不用改動外部接口,因此設計更靈活,更富有彈性函數對象也具備有存儲先前調用結果的數據成員。在使用普通函數時需要將先前調用的結果存儲在全程或者本地靜態變量中,但是全程或者本地靜態變量有某些我們不願意看到的缺陷。
  • 其次,在函數對象中編譯器能實現內聯調用,從而更進一步增強了性能。這在函數指針中幾乎是不可能實現的。
  • C++11還提供了limbda表達式來實現函數的靈活調用。詳見《C++ Primer Plus》第18章。

----------------------------------------------------------------------------------------------------------------------------------------------------------

 std::function是一組函數對象包裝類的模板,實現了一個泛型的回調機制。function與函數指針比較相似,優點在於它允許用戶在目標的實現上擁有更大的彈性,即目標既可以是普通函數,也可以是函數對象和類的成員函數,而且可以給函數添加狀態。
聲明一個function時,需要給出所包裝的函數對象的返回值類型和各個參數的類型。比如,聲明一個function,它返回一個bool類型並接受一個int類型和一個float類型的參數,可以像下面這樣:
function<bool (int, float)> f;

下面簡要介紹一下function的比較重要的幾個接口。

--------------------------------------------------------------------------------

function();
缺省構造函數,創建一個空的函數對象。如果一個空的function被調用,將會拋出一個類型為bad_function_call的異常。
--------------------------------------------------------------------------------
template <typename F> function(F g);

這個泛型的構造函數接受一個兼容的函數對象,即這樣一個函數或函數對象,它的返回類型與被構造的function的返回類型或者一樣,或者可以隱式轉換,並且它的參數也要與被構造的function的參數類型或者一樣,或者可以隱式轉換。

注意,也可以使用另外一個function實例來進行構造。這樣做,並且function g為空,則被構造的function也為空。使用空的函數指針和空的成員函數指針也會產生空的function。如果這樣做,並且function g為空,則被構造的function也為空。使用空的函數指針和空的成員函數指針也會產生空的function。

--------------------------------------------------------------------------------

template <typename F> function(reference_wrapper<F> g);
這個構造函數與前一個類似,但它接受的函數對象包裝在一個reference_wrapper中,用以避免通過值來傳遞而產生函數或函數對象的一份拷貝。這同樣要求函數對象兼容於function的簽名。
--------------------------------------------------------------------------------
function& operator=(const function& g);
賦值操作符保存g中的函數或函數對象的一份拷貝;如果g為空,被賦值的函數也將為空。
--------------------------------------------------------------------------------
template<typename F> function& operator=(F g);
這個泛型賦值操作符接受一個兼容的函數指針或函數對象。
注意,也可以用另一個 function 實例(帶有不同但兼容的簽名)來賦值。這同樣意味着,如果g是另一個function實例且為空,則賦值后的函數也為空。賦值一個空的函數指針或空的成員函數指針也會使function為空。
--------------------------------------------------------------------------------
bool empty() const;
這個成員函數返回一個布爾值,表示該function是否含有一個函數或函數對象。如果有一個目標函數或函數對象可被調用,它返回 false 。因為一個function可以在一個布爾上下文中測試,或者與0進行比較,因此這個成員函數可能會在未來版本的庫中被取消,你應該避免使用它。
--------------------------------------------------------------------------------
void clear();
這個成員函數清除 function, 即它不再關聯到一個函數或函數對象。如果function已經是空的,這個調用沒有影響。在調用后,function肯定為空。令一個function為空的首選方法是賦0給它;clear 可能在未來版本的庫中被取消。
--------------------------------------------------------------------------------
result_type operator()(Arg1 a1, Arg2 a2, ..., ArgN aN) const;
調用操作符是調用function的方法。你不能調用一個空的 function ,那樣會拋出一個bad_function_call的異常。調用操作符的執行會調用function中的函數或函數對象,並返回它的結果。
--------------------------------------------------------------------------------
下面分別給出使用function來包裝普通函數,函數對象和類的成員函數的參考代碼。
1、普通函數
1 int Add(int x, int y) 2 
3 { 4    return x+y; 5 } 6 function<int (int,int)> f = Add; 7 int z = f(2, 3);

 

2、函數對象

 1 class CStudent 2 { 3 public: 4             void operator() (string strName, int nAge) 5 { 6                 cout << strName << " : " << nAge << endl; 7 } 8 }; 9 
10 CStudent stu; 11 function<void (string, int)> f = stu; 12 f("Mike",  12);

 

3、類的成員函數

 1 struct TAdd 2 { 3     int Add(int x,int y) 4 { 5         return x+y; 6 } 7 }; 8 
 9 function<int  (TAdd *, int, int)> f = TAdd::Add; 10 TAdd tAdd; 11 f(&tAdd, 2, 3);   // 如果前面的模板參數為傳值或引用,直接傳入tAdd即可

 

接下來我們來看看使用function來保存函數對象狀態的情況。考慮下面的代碼:

 1 class CAdd 2 { 3 public: 4     CAdd():m_nSum(0) { NULL; } 5     int operator()(int i)  //重載 () 運算符 6 { 7           m_nSum += i; 8           return m_nSum; 9 } 10 
11     int Sum() const 
12 { 13         return m_nSum; 14 } 15 
16 private: 17     int m_nSum; 18 }; 19 
20 int main() 21 { 22 CAdd add; 23     function<int (int)> f1 = add; 24     function<int (int)> f2 = add; 25     cout << f1(10) << "," << f2(10) << "," << add.Sum() << endl; 26     return 0; 27 }
可能和大家想象的結果不一樣,上面程序的輸出是:10,10,0。我們將同一個函數對象賦值給了兩個function,然后分別調用了這兩個function,但函數對象中m_nSum的狀態並沒有被保持,問題出在哪兒呢?這是因為function的缺省行為是拷貝一份傳遞給它的函數對象,於是f1和f2中保存的都是add對象的拷貝,調用f1和f2后,add對象中的值並沒有被修改。

C++ 11中提供了ref和cref函數,來提供對象的引用和常引用的包裝。要使function能夠正確地保存函數對象的狀態,我們可以這樣來修改代碼:

1 CAdd add; 2 function<int(int)> f1 = ref(add); 3 function<int(int)> f2 = ref(add);
另外,在兩個function之間賦值時,如果源function保存的是函數對象的拷貝,則目標function保存的也是函數對象的拷貝;如果源function保存的是函數對象的引用,則目標function保存的也是函數對象的引用。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM