為什么我們需要使用仿函數(shù)(仿函數(shù)解決了什么痛點(diǎn))?
成都創(chuàng)新互聯(lián)2013年至今,先為羅平等服務(wù)建站,羅平等地企業(yè),進(jìn)行企業(yè)商務(wù)咨詢服務(wù)。為羅平企業(yè)網(wǎng)站制作PC+手機(jī)+微官網(wǎng)三網(wǎng)同步一站式服務(wù)解決您的所有建站問題。仿函數(shù)的優(yōu)點(diǎn)和作用?
文章目錄場景引入
計(jì)算a數(shù)組里面全部的和,放在x變量里面。
#include#include#include
using namespace std;
templateinline T accumulate(InputIterator first, InputIterator last, T init, T (*ptrA)(T, T)) {//函數(shù)指針
while (first != last) {init = (*ptrA)(init, *first);
++first;
}
return init;
}
int funcA(int x, int y)
{return x + y;
}
int main(void)
{int a[5] = {2, 5, 7, 9, 11};
random_shuffle(&a[0], &a[5]);
int x = ::accumulate(&a[0], &a[5], 0, funcA);
cout<< x<< endl;
return 0;
}
#include#include#include
using namespace std;
templateinline T accumulate(InputIterator first, InputIterator last, T init, FunObject object) {//函數(shù)對象
while (first != last) {init = object(init, *first);
++first;
}
return init;
}
template< typename T>class Test {public:
T operator()(const T& x, const T& y) {return x + y;
}
};
int main(void)
{int a[5] = {2, 5, 7, 9, 11};
random_shuffle(&a[0], &a[5]);
int x = ::accumulate(&a[0], &a[5], 0, Test()); //仿函數(shù)作為函數(shù)的入?yún)?,只需要傳入類對象即可,這里傳入的是匿名對象
cout<< x<< endl;
return 0;
}
這樣就解決了效率和函數(shù)重載的問題了。
場景二#includeusing namespace std;
int RecallFunc(int *start, int *end, bool (*pf)(int)) {int count=0;
for(int *i = start; i != end+1; i++) {count = pf(*i) ? count+1 : count;
}
return count;
}
bool IsGreaterThanTen(int num) {return num>10 ? true : false;
}
int main() {int a[5] = {10,100,11,5,19};
int result = RecallFunc(a, a+4, IsGreaterThanTen);
cout<
bool IsGreaterThanThreshold(int num, int threshold) {return num>threshold ? true : false;
}
(1)閾值作為函數(shù)的局部變量。局部變量不能在函數(shù)調(diào)用中傳遞,故不可行;
bool IsGreaterThanThreshold(int num) {int threshold; // 這里的threhold 沒法獲得外部的傳參
return num>threshold ? true : false;
}
(2)全局變量。我們可以將閾值設(shè)置成一個(gè)全局變量。這種方法雖然可行,但不優(yōu)雅,且容易引入 Bug,比如全局變量容易同名,造成命名空間污染
int threshold=10; // 定義全局變量
bool IsGreaterThanThreshold(int num) {
return num>threshold ? true : false;
}
(3)函數(shù)傳參。這種方法我們已經(jīng)討論過了,多個(gè)參數(shù)不適用于已定義好的 RecallFunc() 函數(shù)。(除非你重寫函數(shù)指針)
假設(shè)你設(shè)計(jì)的傳參函數(shù)是這樣的:
bool IsGreaterThanThreshold(int num, int threshold) {
return num>threshold ? true : false;
}
在此基礎(chǔ)上,你必須重寫 RecallFunc() 函數(shù) 。
int RecallFunc(int *start, int *end, bool (*pf)(int,int),int threshold) {這里就需要引入新參數(shù),來指threshold。同時(shí)需要重寫函數(shù)指針,以使其符合IsGreaterThanThreshold 。
int count=0;
for(int *i = start; i != end+1; i++) {count = pf(*i,threshold) ? count+1 : count;
}
return count;
}
這種方法擴(kuò)展性較差,當(dāng)函數(shù)參數(shù)有所變化,則無法兼容舊的代碼,具體在第一小節(jié)已經(jīng)闡述。正如上面的例子,在我們寫代碼時(shí)有時(shí)會發(fā)現(xiàn)有些功能代碼,會不斷地被使用。為了復(fù)用這些代碼,實(shí)現(xiàn)為一個(gè)公共的函數(shù)是一個(gè)解決方法。不過函數(shù)用到的一些變量,可能是公共的全局變量。引入全局變量,容易出現(xiàn)同名沖突,不方便維護(hù)。
這時(shí)就可以使用仿函數(shù)了,寫一個(gè)簡單類,除了維護(hù)類的基本成員函數(shù)外,只需要重載 operator() 運(yùn)算符 。這樣既可以免去對一些公共變量的維護(hù),也可以使重復(fù)使用的代碼獨(dú)立出來,以便下次復(fù)用。而且相對于函數(shù)更優(yōu)秀的性質(zhì),仿函數(shù)還可以進(jìn)行依賴、組合與繼承等,這樣有利于資源的管理。如果再配合模板技術(shù)和 Policy 編程思想,則更加威力無窮,大家可以慢慢體會。Policy 表述了泛型函數(shù)和泛型類的一些可配置行為(通常都具有被經(jīng)常使用的缺省值)。
STL 中也大量涉及到仿函數(shù),有時(shí)仿函數(shù)的使用是為了函數(shù)擁有類的性質(zhì),以達(dá)到安全傳遞函數(shù)指針、依據(jù)函數(shù)生成對象、甚至是讓函數(shù)之間有繼承關(guān)系、對函數(shù)進(jìn)行運(yùn)算和操作的效果。比如 STL 中的容器 set 就使用了仿函數(shù) less ,而 less 繼承的 binary_function,就可以看作是對于一類函數(shù)的總體聲明,這是函數(shù)做不到的。
#includeusing namespace std;
class IsGreaterThanThresholdFunctor {public:
explicit IsGreaterThanThresholdFunctor(int t):threshold(t){}
bool operator() (int num) const {return num >threshold ? true : false;
}
private:
const int threshold;
};
int RecallFunc(int *start, int *end, IsGreaterThanThresholdFunctor myFunctor) {int count = 0;
for (int *i = start; i != end + 1; i++) {count = myFunctor(*i) ? count + 1 : count;
}
return count;
}
int main() {int a[5] = {10,100,11,5,19};
int result = RecallFunc(a, a + 4, IsGreaterThanThresholdFunctor(10));//仿函數(shù)作為函數(shù)的入?yún)?,只需要傳入類對象即可,這里傳入的是匿名對象
cout<< result<< endl;
}
仿函數(shù)(Functor)又稱為函數(shù)對象(Function Object)是一個(gè)能行使函數(shù)功能的類。
仿函數(shù)的語法幾乎和我們普通的函數(shù)調(diào)用一樣,不過作為仿函數(shù)的類,都必須重載 operator() 運(yùn)算符。因?yàn)檎{(diào)用仿函數(shù),實(shí)際上就是通過類對象調(diào)用重載后的 operator() 運(yùn)算符。
我們先來看一個(gè)仿函數(shù)的例子。
直接調(diào)用仿函數(shù)class StringAppend {public:
explicit StringAppend(const string& str) : ss(str){}
void operator() (const string& str) const { cout<< str<< ' '<< ss<< endl;
}
private:
const string ss;
};
int main() {StringAppend myFunctor2("and world!");
myFunctor2("Hello");// 隱式寫法
// myFunctor2.operator()("Hello"); //顯式寫法
編譯運(yùn)行輸出:
Hello and world!
仿函數(shù)作為函數(shù)入?yún)?pre>#includeusing namespace std;
class IsGreaterThanThresholdFunctor {public:
explicit IsGreaterThanThresholdFunctor(int t):threshold(t){}
bool operator() (int num) const {return num >threshold ? true : false;
}
private:
const int threshold;
};
int RecallFunc(int *start, int *end, IsGreaterThanThresholdFunctor myFunctor) {int count = 0;
for (int *i = start; i != end + 1; i++) {count = myFunctor(*i) ? count + 1 : count;
}
return count;
}
int main() {int a[5] = {10,100,11,5,19};
int result = RecallFunc(a, a + 4, IsGreaterThanThresholdFunctor(10));//仿函數(shù)作為函數(shù)的入?yún)?,只需要傳入類對象即可,這里傳入的是匿名對象
cout<< result<< endl;
}
仿函數(shù)和STL通過仿函數(shù)建立與stl溝通的橋梁,只為算法服務(wù),當(dāng)我需要對算法提出一些要求的時(shí)候,例如排序默認(rèn)為從小到大,但是我需要由大到小進(jìn)行排序,就需要用一般函數(shù)的形式或仿函數(shù)的形式告訴算法,實(shí)現(xiàn)第二個(gè)版本的算法。
為什么要把加減,比大小等等功能定義成一個(gè)函數(shù)或仿函數(shù),因?yàn)樾枰獙⑦@些信息傳入算法中。算法拿到這些東西之后才會做出相對應(yīng)的改變。
STL內(nèi)建的算術(shù)類仿函數(shù),支持加法、減法、乘法、除法、模數(shù)(余數(shù))、否定運(yùn)算。
templateT plus????//加法仿函數(shù)
templateT minus????//減法仿函數(shù)
templateT multiplies?//乘法仿函數(shù)
templateT divides???//除法仿函數(shù)
templateT modulus???//取模仿函數(shù)
templateT negate????//取反仿函數(shù)
struct plus : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct minus : public binary_function{T operator()(const T &x, const T &y) const return x - y;
};
templatestruct multiplies : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct divides : public binary_function{T operator()(const T &x, const T &y) const return x y;
};
templatestruct modulus : public binary_function{T operator()(const T &x, const T &y) const return x $y;
};
templatestruct negate : public unary_function{T operator()(const T &x) const return -x;
};
#include#includeusing namespace std;
void test1()
{negaten;
cout<< n(50)<< endl;
}
void test2()
{plusp;
cout<< p(10, 20)<< endl;
}
int main()
{test1();
test2();
std::cout<< "Hello World!\n";
}
// -50
// 30
關(guān)系運(yùn)算符STL支持6種關(guān)系運(yùn)算,每一種都是二元運(yùn)算
等于,不等于,大于,大于等于,小于,小于等于
templatestruct equal_to:public binary_function{bool operator()(const T&x,const T& y)const {return x==y;};
}
templatestruct not_equal_to:public binary_function{bool operator()(const T& x,const T& y)const {return x!=y;}
};
templatestruct greater:public binary_function{bool operator()(const T&x ,const T7 y)const {return x>y;}
};
templatestruct less:public binary_function{bool operator()(const T&x ,const T7 y)const {return xstruct greater_equal:public binary_function{bool operator()(const T&x ,const T7 y)const {return x>=y;}
};
templatestruct less_equal:public binary_function{bool operator()(const T&x ,const T7 y)const {return x<=y;}
};
邏輯運(yùn)算符templatebool logical_and//邏輯與
templatebool logical_or //邏輯或
templatebool logical_not//邏輯非
仿函數(shù)和智能指針
仿函數(shù)自定義刪除器//用來釋放malloc出來的函數(shù)對象
templateclass FreeFunc{public:
void operator()(T* ptr)
{cout<< "free:"<< ptr<< endl;
free(ptr);
}
};
//用來釋放new[]出來的函數(shù)對象
templateclass DeleteArrayFunc {public:
void operator()(T* ptr)
{cout<< "delete[]"<< ptr<< endl;
delete[] ptr;
}
};
//用來釋放new 出來的函數(shù)對象
templateclass DeleteArrayFunc {public:
void operator()(T* ptr)
{cout<< "delete "<< ptr<< endl;
delete ptr;
}
};
//用來釋放文件描述符的函數(shù)對象
templateclass ClosefdFunc{public:
void operator()(T* fd)
{cout<< "close fd"<< fd<< endl;
fclose(fd);
}
};
void test06(){FreeFuncObject1;
shared_ptr sp1((int*)malloc(sizeof(int)*4), Object1); // 回調(diào)函數(shù)是可調(diào)用對象,可以是普通的函數(shù)名或者函數(shù)對象或者lambda表達(dá)式
DeleteArrayFuncObject2;
shared_ptr sp2(new int[4], Object2);
ClosefdFuncObject3;
shared_ptr sp3(fopen("myfile.txt","w"), Object3);
}
int main()
{test06();
return 0;
}
close fd0x7ff94b4bfa90
delete[]0x220c21d1ae0
free:0x220c21d1770
智能指針中的仿函數(shù)//智能指針的刪除器:
templatestruct default_delete{//......
//default deleter for unique_ptr,其中_Ptr是智能指針底層資源的指針
void operator()(_Ty *_Ptr) const _NOEXCEPT
{// delete a pointer
delete _Ptr;
//默認(rèn)刪除器僅僅只做一件事,只調(diào)用delete進(jìn)行資源的釋放
}
//......
};
改進(jìn),針對這種特殊的情況,添加自定義的一個(gè)刪除器保證資源釋放完全:
templateclass Deleter{public:
void operator()(Ty *ptr)const{cout<<"Call a custom method !!!!! "<std::unique_ptr>ptr(new int[100]);//delete []ptr
return 0;
}
或者使用 default_delete來做刪除器
// 可用default_delete來做刪除器,default_.delete是標(biāo)準(zhǔn)庫里的模板類。
void fun4()
{cout<< "detail5:: func4()"<< endl;
shared_ptrpi5(new int[100](), std::default_delete());
}
改進(jìn),爭對這種特殊的情況,添加自定義的一個(gè)刪除器保證資源釋放完全:
templateclass Deleter{public:
void operator()(Ty *ptr)const{cout<<"Call a custom method !!!!! "<std::unique_ptr>ptr(new int[100]);//delete []ptr
return 0;
}
或者使用 default_delete來做刪除器
// 可用default_delete來做刪除器,default_.delete是標(biāo)準(zhǔn)庫里的模板類。
void fun4()
{cout<< "detail5:: func4()"<< endl;
shared_ptrpi5(new int[100](), std::default_delete());
}
你是否還在尋找穩(wěn)定的海外服務(wù)器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機(jī)房具備T級流量清洗系統(tǒng)配攻擊溯源,準(zhǔn)確流量調(diào)度確保服務(wù)器高可用性,企業(yè)級服務(wù)器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧