一文詳解C++仿函數(shù)
一、仿函數(shù)的定義
在C++中,仿函數(shù)(Functors)或稱為函數(shù)對象(Function Objects)是重載了調用操作符operator()的類或結構體,這使得這些類的對象可以像函數(shù)一樣被調用。仿函數(shù)的主要用途是提供一種靈活的方式來定義和操作數(shù)據(jù)。通過創(chuàng)建自定義的仿函數(shù),你可以將待定的邏輯封裝在一個對象中,并在需要時將其傳遞給算法或容器。
二、仿函數(shù)的特性
我們用例子來解釋
- 例子一
#include <iostream> #include <vector> #include <algorithm> using namespace std; //定義一個仿函數(shù)用來比較兩個整數(shù) struct CompareInt{ bool operator()(int a,int b) const{ return a<b; } }; int main() { vector<int> numbers={5,2,9,1,5,6}; //使用STL中的sort算法和自定義的仿函數(shù)對vector進行排序 std::sort(numbers.begin(),numbers.end(),CompareInt()); //輸出排序后的vector for(int num:numbers) { cout<<num<<" "; } cout<<endl; return 0; }
在以上這個例子中我們的仿函數(shù)結構體中重載了小括號(),傳入了兩個參數(shù),達到完成比較兩個數(shù)大小的功能,當我們在主函數(shù)std::sort(numbers.begin(),numbers.end(),CompareInt())中,CompareInt看似是一個對象,其實它是一個仿函數(shù),函數(shù)的返回值是bool類型的,傳入的bool類型的值決定了是升序還是倒序。
- 例子二 仿函數(shù)可以封裝狀態(tài)
仿函數(shù) 的一個關鍵特點是它可以封裝狀態(tài),這意味著它們可以擁有數(shù)據(jù)成員,這些成員可以在不同的函數(shù)調用之間保持狀態(tài),以下是一個簡單的C++仿函數(shù)示例,它包含了一個狀態(tài)變量(internal_sum),并在每次調用的時候將其與輸入?yún)?shù)相加:
#include <iostream> using namespace std; //定義一個仿函數(shù)類,帶有內部狀態(tài) class Accumulator{ //構造函數(shù),初始化內部狀態(tài) public: Accumulator(int initial_sum=0) : internal_sum(initial_sum) {} //重載operator(),使其接受一個整數(shù)參數(shù),將其與內部狀態(tài)相加,并返回結果 int operator()(int value){ internal_sum+=value; //修改內部狀態(tài) return internal_sum; //返回累加后的結果 } //獲取當前內部狀態(tài) int get_sum() const { return internal_sum; } private: int internal_sum;//仿函數(shù)的內部狀態(tài) }; int main() { //創(chuàng)建Accumulator類的實例,初始化內部狀態(tài)為0 Accumulator accumulator; //使用仿函數(shù)來調用它,即使用它跟使用一個函數(shù)一樣 cout<<"After adding 5:"<<accumulator(5)<<endl; cout<<"After adding 3:"<<accumulator(3)<<endl; cout<<"Current sum:"<<accumulator.get_sum()<<endl; //創(chuàng)建另一個Accumulator實例,初始化內部狀態(tài)為10 Accumulator accumulator2(10); //使用這個實例進行累加 cout<<"initial sum 10,after adding 2:"<<accumulator2(2)<<endl; return 0; }
在這個例子中,Accumulator類是一個仿函數(shù),它有一個私有的整數(shù)成員internal_sum,用于存儲累加的值,構造函數(shù)允許我們初始化internal_sum的值,我們每調用依次這個仿函數(shù),它就會相應的累加,當然我們創(chuàng)建的第二個累加器的實例對象,它們兩個的值互不影響。
- 仿函數(shù)可以當參數(shù)傳遞
#include <iostream> // 定義一個仿函數(shù)類 class MyFunctor { public: // 重載 operator() int operator()(int value) const { return value * 2; } }; // 定義一個接受仿函數(shù)作為參數(shù)的函數(shù) void applyFunctor(const MyFunctor& functor, int value) { std::cout << "經(jīng)過仿函數(shù)處理過后的值: " << functor(value) << std::endl; } int main() { // 創(chuàng)建仿函數(shù)實例 MyFunctor functor; // 調用接受仿函數(shù)為參數(shù)的函數(shù) applyFunctor(functor, 5); return 0; }
在以上這個例子中,我們定義了一個仿函數(shù)類,里面重載了operator(),使得我們在實例化函數(shù)對象時,調用這個
- 仿函數(shù)可以作為模板參數(shù)
仿函數(shù)確實可以作為模板參數(shù)使用,因為它們具有自己的唯一類型。模板參數(shù)可以接受任何類型,包括類類型,而仿函數(shù)就是類類型的一種。下面是一個代碼樣例,展示了如何使用仿函數(shù)作為模板參數(shù):
#include <iostream> #include <vector> #include <algorithm> // 定義一個仿函數(shù)類,用于將整數(shù)乘以2 class MultiplyByTwo { public: // 重載 operator(),實現(xiàn)對傳入整數(shù)的乘以2操作 int operator()(int value) const { return value * 2; } }; // 定義一個接受仿函數(shù)作為模板參數(shù)的函數(shù)模板 // 該函數(shù)模板用于對 vector 中的每個元素應用仿函數(shù)進行變換 template <typename Functor> void transformVector(std::vector<int> &vec, Functor functor) { // 遍歷 vector 中的每個元素,并應用仿函數(shù)進行變換 for (auto &item : vec) { item = functor(item); } } int main() { // 初始化一個包含整數(shù)的 vector std::vector<int> vec = {1, 2, 3, 4, 5}; // 使用 MultiplyByTwo 仿函數(shù)類作為模板參數(shù),調用 transformVector 函數(shù) // 對 vector 中的每個元素進行乘以2的操作 transformVector(vec, MultiplyByTwo()); // 輸出變換后的 vector 元素 for (auto item : vec) { std::cout << item << " "; } std::cout << std::endl; //也可以直接使用lambda表達式作為模板參數(shù) transformVector(vec,[](int value){return value*3;}); //再次輸出變換后的值 for (auto item : vec) { std::cout << item << " "; } std::cout << std::endl; return 0; }
在這個例子中,transformVector是一個函數(shù)模板,它接受一個std::vector和一個仿函數(shù)作為參數(shù)。這個函數(shù)模板使用仿函數(shù)來變換vector中的每一個元素。在主函數(shù)中,我們首先使用MultiplyByTwo仿函數(shù)作為模板參數(shù)調用transformVector函數(shù),然后使用一個lambda表達式作為模板參數(shù)再次調用transformVector函數(shù)。這展示了仿函數(shù)和lambda表達式都可以作為模板參數(shù)傳遞給函數(shù)模板
- 謂詞
在計算機語言中,謂詞通常指條件表達式的求值返回真或假的過程
1.一元謂詞(Unary Predicate)
一元謂詞通常用于判斷單個元素是否滿足某個條件,以下是一個使用std::remove_if和一元謂詞的簡單例子,它移除std::vector中的所有偶數(shù)
#include <iostream> #include <vector> #include <algorithm> using namespace std; // 定義一元謂詞函數(shù),檢查整數(shù)是否為偶數(shù) // 參數(shù): // - num: 需要檢查的整數(shù) // 返回值: // - 如果 num 是偶數(shù),返回 true;否則返回 false bool isEven(int num) { return num % 2 == 0; } int main() { // 初始化一個包含整數(shù)的 vector vector<int> vec{1, 2, 3, 4, 5, 6, 7, 8, 9}; // 使用 std::remove_if 和一元謂詞 isEven 移除所有偶數(shù) // std::remove_if 將所有滿足條件(即偶數(shù))的元素移動到 vector 的末尾,并返回新末尾的迭代器 // vec.erase 則刪除從新末尾到原末尾的所有元素,從而實現(xiàn)移除操作 vec.erase(remove_if(vec.begin(), vec.end(), isEven), vec.end()); // 輸出處理之后的 vector 元素 for (auto i : vec) { cout << i << " "; } cout << endl; return 0; }
2.二元謂詞(Binary Predicate)
二元謂詞通常用于比較兩個元素,以下是一個使用std::sort和二元謂詞的簡單例子,它根據(jù)自定義的比較規(guī)則對vector進行排序:
#include <iostream> #include <vector> #include <algorithm> using namespace std; // 定義二元謂詞函數(shù),用于比較兩個整數(shù)的大小(降序排序) // 參數(shù): // - a: 第一個整數(shù) // - b: 第二個整數(shù) // 返回值: // - 如果 a 大于 b,返回 true;否則返回 false bool CompareInt(int a, int b) { return a > b; } int main() { // 初始化一個包含整數(shù)的 vector vector<int> vec = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5}; // 使用 std::sort 和二元謂詞 CompareInt 對 vector 進行降序排序 // std::sort 接受三個參數(shù):起始迭代器、結束迭代器和比較函數(shù) // 比較函數(shù) CompareInt 確定了排序順序為降序 std::sort(vec.begin(), vec.end(), CompareInt); // 輸出處理之后的 vector 元素 for (auto i : vec) { cout << i << " "; } cout << endl; return 0; }
在這兩個例子中,我分別定義了一個一元謂詞和一個二元謂詞,一元謂詞用于判斷單個元素是否為偶數(shù),而二元謂詞用于比較兩個元素的大小,然后,我們使用這些謂詞與標準庫算法結合在一起,實現(xiàn)相應的功能。
三、仿函數(shù)的相對性能優(yōu)勢
- 通常說仿函數(shù)(也稱為函數(shù)對象或functor)比函數(shù)指針執(zhí)行速度快,這并不是一個絕對的結論。在大多數(shù)情況下,它們之間的性能差異是非常小的,甚至在現(xiàn)代的編譯器優(yōu)化下可能幾乎無法測量。但是,有幾個原因可能會導致在某些特定情況下仿函數(shù)相對更快:
- 內聯(lián)優(yōu)化:在仿函數(shù)作為類成員函數(shù)或重載操作符,更有可能被編譯器內聯(lián)(inline),這可以減少函數(shù)調用的開銷。相比之下,函數(shù)指針指向的函數(shù)通常不會被內聯(lián),除非編譯器特別確定這樣做是安全的。
- 狀態(tài)局部性:仿函數(shù)可以包含狀態(tài)(即數(shù)據(jù)成員),這些狀態(tài) 可以與其成員函數(shù)緊密的存儲在一起。這種緊密性可以提高數(shù)據(jù)訪問的局部性,從而提高緩存利用率,進而可能提高性能。
- 類型安全:仿函數(shù)可以通過模板和類型系統(tǒng)提供更高級別的類型安全性。雖然這不會直接影響執(zhí)行速度,但它可以減少因類型錯誤而導致的運行時開銷。
- 無額外的間接引用:函數(shù)指針需要額外的簡介引用來訪問目標函數(shù)。雖然這種間接引用在現(xiàn)代處理器上通常是非??斓模谀承O端情況下,它可能會成為性能瓶頸。
- 編譯時優(yōu)化:由于仿函數(shù)是類的實例,編譯器可以對它們進行更廣泛的優(yōu)化,例如通過消除虛擬函數(shù)調用的開銷(如果仿函數(shù)沒有使用虛函數(shù))或使用特定的類型特定優(yōu)化。
總結
本文主要介紹了 C++ 中的仿函數(shù),包括仿函數(shù)的定義、特性和相對性能優(yōu)勢。仿函數(shù)是重載了調用操作符 operator () 的類或結構體,可像函數(shù)一樣被調用,主要用途是提供靈活方式定義和操作數(shù)據(jù)。其特性有可封裝狀態(tài)、能當參數(shù)傳遞、可作為模板參數(shù)及作為謂詞使用。相對性能優(yōu)勢方面,可能比函數(shù)指針執(zhí)行速度快,原因包括內聯(lián)優(yōu)化、狀態(tài)局部性、類型安全、無額外間接引用和編譯時優(yōu)化等。
到此這篇關于一文詳解C++仿函數(shù)的文章就介紹到這了,更多相關C++仿函數(shù)內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
VSCode配置C++環(huán)境的方法步驟(MSVC)
這篇文章主要介紹了VSCode配置C++環(huán)境的方法步驟,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-05-05