標籤:
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儲存的也是函數對象的引用。
轉載 http://www.cnblogs.com/hujian/archive/2012/12/07/2807605.html
如有著作權問題,請聯絡QQ 858668791
C++ 11 function