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