函數對象:定義了調用操作符()的類對象。當用該對象調用此操作符時,其表現形式如同普通函數調用一般,因此取名叫函數對象。舉個最簡單的例子:
class A
{
public:
int operator() ( int val )
{
return val > 0 ? val : -val;
}
};
類A中定義了操作符 ( ),A對象調用語句在形式上跟以下函數的調用完全一樣:
int i = -1;
A func;
cout << func(i);
與普通函數相比,函數對象比函數更加靈活,函數對象的優勢:
- 函數對象可以有自己的狀態。我們可以在類中定義狀態變量,這樣一個函數對象在多次的調用中可以共享這個狀態;
- 函數對象有自己特有的類型。我們可以傳遞相應的類型作爲參數來實例化相應的模板,比如說帶參數的函數形參。
使用函數對象的典型例子:
1、字符串排序規則
在容器set 中對string 進行排序,首先來定義相應的類並定義 () 來規定排序規則:
class Sort
{
public:
bool operator() (const string &str1, const string &str2) const //帶兩個參數
{
return str1 > str2;
}
};
然後我們可以用這個類作爲參數來初始化set容器:
set<string, Sort> myset; //帶比較函數的set構造函數,並用函數對象Sort初始化
myset.insert("A");
myset.insert("B");
這樣容器內容輸出爲:B,A。
2、謂詞函數
謂詞函數通常用來對傳進來的參數進行判斷,並返回布爾值。但是一般的函數形式固化,比如字符串長度比較只能判斷是否大於一個確定的長度值。函數對象可以作爲謂詞函數,並可以在類初始化時傳遞參數,如字符串長度參考值,因此函數對象比普通函數更加靈活。
現在假設我們有一串數字,要從中找出第一個不小於10的數字。可以定義如下相應的類:
class Upper
{
public:
Upper(int min = 0):m_min(min){}
bool operator() (int value) const
{
return value >= m_min;
}
private:
int m_min;
};
從而這樣調用 find_if 函數:
find_if( dest.begin(), dest.end(), Upper(10) );
首先生成類 Upper 的對象,並用 10 初始化,調用find_if 時將用該函數對象進行判斷。
請注意:在調用用到函數對象的標準庫算法時,除非顯式地指定模板類型爲傳引用,否則默認情況下函數對象是按值傳遞的!因此,如果傳遞一個具有內部狀態的函數對象,則被改變狀態的是函數內部被複制的臨時對象,函數結束後隨之消失。真正傳進來的函數對象狀態併爲改變。
class B
{
public:
B(int n=0) : th(n),count(1) {}
bool operator() (int)
{
return count++ == th;
}
int getCount() const
{
return count;
}
private:
int th;
int count;
};
測試如下:
vector<int> vec;
for( int i = 3; i!= 13; ++i )
vec.push_back(i);
B b(3);
vector<int>::iterator iter = find_if( vec.begin(), vec.end(), b ); //調用函數對象,查找第三個數字
cout<< "3rd:" << *iter <<endl;
cout<< "State:" << b.getCount() <<endl; //指向函數對象內容,但內部值卻爲改變
輸出結果爲,確實能找到第三個數字(5),但查看b的狀態時,返回的 count 依然爲0,說明:
- 在find_if 函數執行期間,內部狀態的臨時對象發生改變;
- 在函數對象調用完成後,臨時對象消失,原來的狀態保持不變。
原則:
不是所有的返回布爾值的函數對象都適合作爲謂詞函數,因此用作謂詞函數的函數對象,最好不要依賴其內部狀態的改變。
標準庫定義的函數對象
標準庫定義了一組算術、關係與邏輯函數對象類。標準庫還定義了一組函數適配器,使我們能夠特化或者擴展標準庫所定義的以及自定義的函數對象類。這些標準庫函數對象類型是在 functional 頭文件中定義的。
plus函數對象的應用:
plus<int> intAdd; // intAdd爲函數對象
int sum = intAdd(10, 20); // 使用函數對象相加兩個操作數
plus<string> stringAdd; //
string strs = stringAdd("ab", "12"); // 使用函數對象相加兩個操作數
函數對象的函數適配器
適配器分爲如下兩類:
1. 綁定器,是一種函數適配器,它通過將一個操作數綁定到給定值而將二元函數對象轉換爲一元函數對象。
2. 求反器,是一種函數適配器,它將謂詞函數對象的真值求反。
綁定器
標準庫定義了兩個綁定器適配器:bind1st 和 bind2nd。每個綁定器接受一個函數對象和一個值。
- bind1st 將給定值綁定到二元函數對象的第一個實參;
- bind2nd 將給定值綁定到二元函數對象的第二個實參。
爲了計算一個容器中所有小於或等於 10 的元素的個數,可以這樣給 count_if 傳遞值:
count_if( vec.begin(), vec.end(), bind2nd( less_equal<int>(), 10 ) );
傳給 count_if 的第三個實參使用 bind2nd 函數適配器,該適配器返回一個函數對象,該對象用 10 作右操作數應用 <= 操作符。這個 count_if 調用計算輸入範圍中小於或等於 10 的元素的個數。
求反器
標準庫還定義了兩個求反器:not1 和 not2。
- not1 將一元函數對象的真值求反;
- not2 將二元函數對象的真值求反。
爲了對 less_equal 函數對象的綁定求反,可以編寫這樣的代碼:
count_if( vec.begin(), vec.end(),not1( bind2nd(less_equal<int>(), 10 ) ) );
首先將 less_equal 對象的第二個操作數綁定到 10,實際上是將該二元操作轉換爲一元操作。再用 not1 對操作的返回值求反,效果是測試每個元素是否 <=。然後,對結果真值求反。這個 count_if 調用的效果是對大於 10 的那些元素進行計數。