仿函數(shù)
定義:仿函數(shù)(functor)鼎俘,就是使一個(gè)類的使用看上去像一個(gè)函數(shù)寥粹。其實(shí)現(xiàn)就是類中實(shí)現(xiàn)一個(gè)operator這個(gè)類就有了類似函數(shù)的行為渣锦,就是一個(gè)仿函數(shù)類了欧芽。
有時(shí)會(huì)發(fā)現(xiàn)有些功能實(shí)現(xiàn)的代碼男韧,會(huì)不斷的在不同的成員函數(shù)中用到朴摊,但是又不好將這些代碼獨(dú)立出來(lái)成為一個(gè)類的一個(gè)成員函數(shù)。但是又很想復(fù)用這些代碼此虑。
寫一個(gè)公共的函數(shù)甚纲,可以,這是一個(gè)解決方法朦前,不過(guò)函數(shù)用到的一些變量介杆,就可能成為公共的全局變量鹃操,再說(shuō)為了復(fù)用這么一片代碼,就要單立出一個(gè)函數(shù)春哨,也不是很好維護(hù)荆隘。
這時(shí)就可以用仿函數(shù)了,寫一個(gè)簡(jiǎn)單類赴背,除了那些維護(hù)一個(gè)[類的成員函數(shù)]外椰拒,就只是實(shí)現(xiàn)一個(gè)operator(),在類實(shí)例化時(shí)凰荚,就將要用的燃观,非參數(shù)的元素傳入類中。這樣就免去了對(duì)一些公共變量的全局化的維護(hù)了便瑟。又可以使那些代碼獨(dú)立出來(lái)缆毁,以便下次復(fù)用。
而且這些仿函數(shù)胳徽,還可以用關(guān)聯(lián)积锅,聚合,依賴的類之間的關(guān)系养盗,與用到他們的類組合在一起缚陷,這樣有利于資源的管理(這點(diǎn)可能是它相對(duì)于函數(shù)最顯著的優(yōu)點(diǎn)了)。如果在配合上模板技術(shù)和policy編程思想往核,那就更是威力無(wú)窮了
#include <iostream>
#include <set>
#include <algorithm>
using namespace std;
class CompareObject{
public:
void operator()(){
cout << "仿函數(shù)" << endl;
}
void operator()(int number,int number2){
cout << "仿函數(shù)" << endl;
}
};
// 查看c++ for_each源碼自定義
class ShowActionObj{
public:
void operator()(int content){
cout << "custom 仿函數(shù)" << content << endl;
}
};
// 回調(diào)方式
void showAction(int content){
cout << "custom 一元謂詞" << content << endl;
}
int main(){
// 謂詞 == 仿函數(shù)
CompareObject fun1;
fun1();
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
for_each(setVar.begin(),setVar.end(),ShowActionObj());
cout << "---" << endl;
for_each(setVar.begin(),setVar.end(),showAction);
return 0;
}
再寫C++ STL 中總結(jié)了謂詞箫爷,相當(dāng)于仿函數(shù)
謂詞 <-> 仿函數(shù)(空謂詞 一元謂詞 二元謂詞 三元謂詞)
C#是通過(guò)委托delegate來(lái)實(shí)現(xiàn)仿函數(shù)的。
Java中的仿函數(shù)是通過(guò)實(shí)現(xiàn)包含單個(gè)函數(shù)的接口實(shí)現(xiàn)的
C語(yǔ)言使用[函數(shù)指針]和[回調(diào)函數(shù)]來(lái)實(shí)現(xiàn)仿函數(shù)聂儒,例如一個(gè)用來(lái)排序的函數(shù)可以這樣使用仿函數(shù)
List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12");
Comparator<String> numStringComparator =new Comparator<String>(){
publicint compare(String o1, String o2){
returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2));
}
};
Collections.sort(list, numStringComparator);
#include <stdlib.h>
/* Callback function */
int compare_ints_function(void*A,void*B)
{
return*((int*)(A))<*((int*)(B));
}
/* Declaration of C sorting function */
void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*));
int main(void)
{
int items[]={4,3,1,2};
sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function);
return 0;
}
回調(diào)函數(shù),謂詞虎锚,仿函數(shù) 分析
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
// 我如何閱讀C++源碼,來(lái)寫我們的仿函數(shù)
// 明明白白的仿函數(shù)(一元謂詞==一元函數(shù)對(duì)象)
class showActionObj {
public:
void operator()(int content) {
cout << "自定義仿函數(shù)" << content << endl;
}
};
// 回調(diào)函數(shù) 如果叫 仿函數(shù) 有點(diǎn)合理
// 簡(jiǎn)潔方式(回調(diào)函數(shù)衩婚、一元謂詞 但是不能稱為 仿函數(shù))
void showAction(int content) {
cout << "自定義 一元謂詞" << content << endl;
}
using namespace std;
int main() {
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
// for_each(setVar.begin(), setVar.end(), showActionObj());
for_each(setVar.begin(), setVar.end(), showAction);
return 0;
}
C++ 中窜护,STL + 算法包 + 迭代器 是分開(kāi)的。所以我們需要手動(dòng)組合非春。
for_each: 源碼
- 回調(diào)函數(shù) (功能夠簡(jiǎn)單)
- 仿函數(shù)(擴(kuò)展性強(qiáng)) C++內(nèi)置源碼使用仿函數(shù)頻率高柱徙,擴(kuò)展性強(qiáng)
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
// 回調(diào)函數(shù) (功能夠簡(jiǎn)單)
void showAction(int __first) {
cout << "一元謂詞" << __first << endl;
}
// 仿函數(shù)(擴(kuò)展性強(qiáng)) C++內(nèi)置源碼使用仿函數(shù)頻率高,擴(kuò)展性強(qiáng)
class showActionObj {
public:
int count = 0;
void _count() { cout << "本次輸出次數(shù)是:" << this->count << endl; }
void operator() (int __first) {
cout << "仿函數(shù)" << __first << endl;
count++;
}
};
int main() {
// 理解:類型傳遞
// set<int, showActionObj> setVar; 這樣寫的語(yǔ)法是OK的奇昙,不能加括號(hào)
set<int> setVar;
setVar.insert(10);
setVar.insert(20);
setVar.insert(30);
setVar.insert(40);
setVar.insert(50);
setVar.insert(60);
// TODO 第一種方式
for_each(setVar.begin(), setVar.end(), showAction);
// 請(qǐng)你統(tǒng)計(jì)打印次數(shù)护侮? 答:做不到
// TODO 第二種方式
showActionObj s; // 理解:值傳遞
s = for_each(setVar.begin(), setVar.end(), s); // 傳入進(jìn)去的s是新的副本,我們外面的s是舊地址
// 請(qǐng)你統(tǒng)計(jì)打印次數(shù)储耐? 答:OK
s._count();
return 0;
}
看下_Function
可以解釋這句話
s = for_each(setVar.begin(), setVar.end(), s); // 傳入進(jìn)去的s是新的副本羊初,我們外面的s是舊地址
仿函數(shù) 能做到的封裝,回調(diào)函數(shù)是做不到的什湘。源碼用到了大量仿函數(shù)长赞,證明它的擴(kuò)展性好晦攒。
解決 賦值問(wèn)題說(shuō)明
類型傳遞
的 仿函數(shù)
怎么看源碼得知寫法
set<string> setVar;
點(diǎn)擊進(jìn)入源碼查看
看到這部分模板定義
template <class _Key, class _Compare = less<_Key>,
class _Allocator = allocator<_Key> >
第一個(gè)參數(shù)就是我們<> 里面的類型,第二個(gè)參數(shù)得哆,可以不寫勤家,默認(rèn)參數(shù)less
less ,public繼承了binary_function
struct _LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool>
{
_LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
bool operator()(const _Tp& __x, const _Tp& __y) const
{return __x < __y;}
};
這樣做,思想和RxJava一致柳恐,做到統(tǒng)一過(guò)濾用,有成百上千的操作符热幔。
父類就可以得到我的子類仿函數(shù)乐设,根據(jù)這個(gè),我們就可以自定義仿函數(shù)绎巨。仿造源碼近尚,寫一個(gè)二元謂詞
#include <iostream>
#include <set>
using namespace std;
// C++源碼:typename _Compare = std::less less內(nèi)置的仿函數(shù),根據(jù)內(nèi)置仿函數(shù)去寫 自定義
// bool operator()(const _Tp& __x, const _Tp& __y) const 二元謂詞
class CompareObjectClass {
public:
bool operator() (const string & __x, const string & __y) const { // const 指針 const 常量指針常量 = 只讀
return __x > __y;
}
};
int main() {
set<string, CompareObjectClass> setVar; // 第一版
setVar.insert(setVar.begin(), "AAAAAAA");
setVar.insert(setVar.begin(), "BBBBBBB");
setVar.insert(setVar.begin(), "CCCCCCC");
setVar.insert(setVar.begin(), "DDDDDDD");
setVar.insert(setVar.begin(), "EEEEEEE");
setVar.insert(setVar.begin(), "FFFFFFF");
// 迭代器 循環(huán)
for (set<string>::iterator iteratorVar = setVar.begin(); iteratorVar != setVar.end(); iteratorVar++) {
cout << "循環(huán)item:" << *iteratorVar << "\t";
// 循環(huán)item:AAAAAAA 循環(huán)item:BBBBBBB 循環(huán)item:CCCCCCC 循環(huán)item:DDDDDDD 循環(huán)item:EEEEEEE 循環(huán)item:FFFFFFF
// 循環(huán)item:FFFFFFF 循環(huán)item:EEEEEEE 循環(huán)item:DDDDDDD 循環(huán)item:CCCCCCC 循環(huán)item:BBBBBBB 循環(huán)item:AAAAAAA
}
return 0;
}
通過(guò)Set 的內(nèi)置反函數(shù)less场勤, 內(nèi)置反函數(shù)戈锻,定義我們自己的函數(shù)
容器存儲(chǔ)對(duì)象,生命周期
set 存入對(duì)象 奔潰(set會(huì)自動(dòng)排序和媳,對(duì)象沒(méi)法排序格遭,所以奔潰) 解決方案:自定義仿函數(shù)解決
為了方便避免重復(fù),我們用vector存儲(chǔ)對(duì)象留瞳,說(shuō)明生命周期
#include <iostream>
#include <set> // set 存入對(duì)象 奔潰(set會(huì)自動(dòng)排序拒迅,對(duì)象沒(méi)法排序,所以奔潰) 解決方案:自定義仿函數(shù)解決
#include <vector> // 存入對(duì)象
using namespace std;
class Person {
private:
string name;
public:
Person(string name) : name(name) {}
void setName(string name) {
this->name = name;
}
string getName() {
return this->name;
}
Person(const Person &person) {
this->name = person.name; // 淺拷貝
cout << "Person拷貝構(gòu)造函數(shù)執(zhí)行了..." << endl;
}
~Person() {
cout << "Person析構(gòu)函數(shù)執(zhí)行了" << endl;
}
};
int main() {
// Java:把對(duì)象存入 添加到 集合
// C++: 調(diào)用拷貝構(gòu)造函數(shù)她倘,存進(jìn)去的是另一個(gè)新的對(duì)象
vector<Person> vectorVar;
// person 被main函數(shù)彈棧 析構(gòu)一次
Person person("David"); // 2 David
// 里面的insert函數(shù)彈棧 析構(gòu)一次
vectorVar.insert(vectorVar.begin(), person); // 外面的person是舊地址璧微,到insert函數(shù)里面的person就是新地址(拷貝構(gòu)造函數(shù) 一次)
person.setName("Kevin"); // 1
// newPerson 被main函數(shù)彈棧 析構(gòu)一次
Person newPerson =
vectorVar.front(); // front里面的person是舊地址, 外面的newPerson就是新地址(拷貝構(gòu)造函數(shù) 一次)
cout << "newPerson:" << newPerson.getName().c_str() << endl;
// 3次析構(gòu)函數(shù) 兩次拷貝構(gòu)造
return 0;
} // main彈棧
代碼執(zhí)行了硬梁,3次析構(gòu)函數(shù) 兩次拷貝構(gòu)造
Person person("David")
Person newPerson =vectorVar.front()
執(zhí)行拷貝構(gòu)造函數(shù) front里面的是舊的地址前硫,外面的newPerson 就是新的地址
Person person("David")
main函數(shù)執(zhí)行完會(huì)析構(gòu)一次
vectorVar.insert(vectorVar.begin(), person) i
nsert 內(nèi)部結(jié)束也會(huì)析構(gòu)一次
Person newPerson =vectorVar.front()
newPerson 被main函數(shù)彈棧 析構(gòu)一次
預(yù)定義函數(shù)
C++ 內(nèi)置函數(shù)
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
int main() {
// "David" + "AAAA" // 運(yùn)算符重載
// C++已經(jīng)提供了 預(yù)定義函數(shù) plus,minus,multiplies,divides,modulus ...
plus<int> add_func;
int r = add_func(1, 1);
cout << r << endl;
plus<string> add_func2;
string r2 = add_func2("AAAA", "BBB");
cout << r2 << endl;
plus<float> add_func3;
float r3 = add_func3(4354.45f, 34.3f);
cout << r3 << endl;
return 0;
}
簡(jiǎn)單的例子,plus 荧止。 我們拼接字符串可能想到運(yùn)算符重載屹电,但是c++內(nèi)置plus解決了這個(gè)問(wèn)題。
有些比如罩息,對(duì)象可能就不適用嗤详,我們可以手寫預(yù)定義函數(shù)
: public binary_function_t<T, T, T>
可以要可以不要
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;
template<typename Arg1, typename Arg2, typename Result>
struct binary_function_t
{
/// 第一個(gè)參數(shù)類型 是底一個(gè)參數(shù)的類型
typedef Arg1 first_argument_type;
//econd_argument_type是第二個(gè)參數(shù)的類型
typedef Arg2 second_argument_type;
/// @c result_type是返回類型
typedef Result result_type;
};
// TODO 對(duì)象 + 對(duì)象
// 1.運(yùn)算符重載
// 2.對(duì)象+對(duì)象 自己去寫仿函數(shù)
template<typename T>
struct plus_d : public binary_function_t<T, T, T>
{
T operator() (const T & x, const T & y) {
return x + y;
}
};
int main() {
plus_d<int> add_func;
int r = add_func(1, 1);
cout << r << endl;
plus_d<string> add_func2;
string r2 = add_func2("AAAA", "BBB");
cout << r2 << endl;
plus_d<float> add_func3;
float r3 = add_func3(4354.45f, 34.3f);
cout << r3 << endl;
return 0;
}