仿函式
1.什么是仿函式
1.定義和作用
仿函式是一種多載了函式呼叫運算子(operator())的類或結構體,它可以像函式一樣被呼叫,仿函式可以在很多STL演算法中使用,例如sort、for_each、transform等,可以自定義排序規則、操作、條件等等,通過仿函式,C++程式員可以更加靈活地實作自己的演算法,
與普通函式不同,仿函式可以保存狀態,因此在使用仿函式時可以靈活地傳遞引數并進行計算,非常適用于一些復雜的演算法和資料結構的實作,
2.仿函式與函式指標的區別
在C++中,函式指標可以作為引數傳遞和回傳值,但是函式指標只能指向函式,無法指向類成員函式和lambda運算式,而仿函式可以作為一種通用的函式封裝,可以指向函式、類成員函式以及lambda運算式,并且可以保存狀態,因此,仿函式比函式指標更加靈活和可擴展,
3.仿函式的呼叫方式
仿函式可以像函式一樣被呼叫
class MyFunctor {
public:
void operator() (int i) {
cout << i << endl;
}
};
MyFunctor myFunctor;
myFunctor(123);
//結果
//123
在STL演算法中,仿函式也可以作為函式引數傳遞
vector<int> vec{3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
sort(vec.begin(), vec.end(), greater<int>());
for_each(vec.begin(), vec.end(), MyFunctor());
其中,greater()是一個內置的仿函式物件,用于實作降序排列,
2.仿函式的分類
1.一元仿函式和二元仿函式
一元仿函式是指只有一個引數的仿函式
template <typename T>
struct MyFunctor1 {
void operator() (T val) {
cout << val << endl;
}
};
template <typename T>
struct MyFunctor2 {
T operator() (T val) {
return val * val;
}
};
二元仿函式是指有兩個引數的仿函式
template <typename T>
struct MyFunctor3 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
在STL演算法中,一元仿函式和二元仿函式通常用于排序、查找、遍歷等操作,
2.函式物件與謂詞
- 函式物件:回傳值為任意型別的仿函式,例如std::plus,std::minus
- 謂詞:回傳值為bool型別的仿函式,例如std::less,std::greater
3.函式配接器
函式配接器是一種特殊的仿函式,它用于將一個仿函式適配到另一個仿函式或函式物件上,STL中常用的函式配接器有:bind1st、bind2nd、not1、not2、logical_and、logical_or等
template <typename T>
struct MyFunctor4 {
bool operator() (T val) {
return val > 0;
}
};
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
count_if(vec.begin(), vec.end(), not1(MyFunctor4<int>()));
//結果
//3
其中,not1是一個函式配接器,它將MyFunctor4適配成一個回傳相反值的仿函式物件,從而統計出vec中小于等于0的元素個數,
3.仿函式的實作
1.多載函式呼叫運算子
仿函式的實作首先要多載函式呼叫運算子(operator()),并根據需要定義引數和回傳值
template <typename T>
struct MyFunctor1 {
void operator() (T val) {
cout << val << endl;
}
};
template <typename T>
struct MyFunctor2 {
T operator() (T val) {
return val * val;
}
};
其中,MyFunctor1是一個一元仿函式,用于輸出引數值,而MyFunctor2是一個一元仿函式,用于計算引數的平方,
2.使用模板類
仿函式通常是一個模板類,可以支持不同的引數型別
template <typename T>
struct MyFunctor3 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
其中,MyFunctor3是一個二元仿函式,用于比較兩個引數的大小,
3.使用函式配接器
仿函式也可以使用函式配接器來實作,例如,使用bind1st函式配接器將一個二元仿函式適配成一個一元仿函式
template <typename T>
struct MyFunctor4 {
bool operator() (T val1, T val2) {
return val1 < val2;
}
};
MyFunctor4<int> myFunctor;
bind1st(myFunctor, 10)(5);
//結果
//false
其中,bind1st將myFunctor適配成一個只有一個引數的仿函式,第一個引數被系結為10,然后用5呼叫這個仿函式,回傳false,
4.實作一個簡單的加法仿函式
class AddFunctor{
public:
AddFunctor(int n):m_n(n){}
int operator()(int x) const{
return x + m_n;
}
private:
int m_n;
}
在上面的代碼中,我們定義了一個AddFunctor類,它帶有一個整型引數n,表示每次呼叫要加上的值,在呼叫運算子()時,回傳x加上m_n的結果,
使用AddFunctor類,可以有如下示例
int main()
{
AddFunctor add(3);
int result = add(5); //結果為8
return 0;
}
4.仿函式的應用
在STL中,許多演算法都需要使用仿函式,例如:
- std::sort():對給定的序列進行排序,需要提供一個比較函式,用于指定排序的規則
- std::for_each():對給定的序列中的每個元素執行指定的操作,需要提供一個函式物件,用于指定操作
- std::find_if():在給定的序列中查找符合指定條件的第一個元素,需要提供一個謂詞,用于指定條件
通過使用仿函式,我們可以將演算法和資料結構解耦,是的演算法更加通用和靈活,
5.仿函式的注意事項
在使用仿函式時需要注意以下幾點
- 仿函式的效率問題:由于仿函式在呼叫時需要進行物件的構造和析構,因此在一些需要頻繁呼叫的場景中,使用仿函式可能會影響演算法的效率
- 仿函式的執行緒安全性:由于仿函式中可能會保存狀態,因此在多執行緒環境下使用時需要注意執行緒安全性問題
6.仿函式的優化
1.使用constexpr
在C++11中,可以使用constexpr關鍵字來宣告一個函式或變數是常量運算式,可以在編譯時計算,如果一個仿函式的operator()是一個常量運算式,可以使用constexpr來進行優化
template <typename T>
struct MyFunctor1 {
constexpr T operator() (T val) const {
return val * val;
}
};
這樣,當使用MyFunctor1時,如果引數是一個常量運算式,那么編譯器就可以在編譯時計算出結果,從而提高程式的執行效率,
2.使用inline
仿函式可以使用inline關鍵字來宣告為行內函式,從而在編譯時將函式體直接嵌入到呼叫處,避免了函式呼叫的開銷
template <typename T>
struct MyFunctor2 {
inline T operator() (T val) const {
return val * val;
}
};
3.使用lambda運算式
C++11引入了lambda運算式,可以方便地定義一個匿名仿函式,與普通的仿函式相比,使用lambda運算式可以減少代碼的冗余,從而提高程式的可讀性和維護性
vector<int> vec{3, -1, 4, -1, 5, -9, 2, -6, 5, 3, -5};
auto count = count_if(vec.begin(), vec.end(), [](int val) { return val > 0; });
其中,lambda運算式[] (int val) { return val > 0; }定義了一個匿名仿函式,用于統計vec中大于0的元素個數,
本文來自博客園,作者:Vergissmeinnicht_z,轉載請注明原文鏈接:https://www.cnblogs.com/Vergissmeinnicht-rj/p/17196619.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/546191.html
標籤:其他
上一篇:專案中多級快取設計實踐總結
下一篇:C語言實作通訊錄
