一、概述
配接器(adaptor)在STL組件的靈活運用功能上,扮演著軸承、轉換器的角色,將一種容器或迭代器裝換或封裝成另一種容器或迭代器。adaptor這個概念,實際上是一種設計模式,其定義如下:
將一個class的接口轉換為另一個class的接口,使原本因接口不兼容而不能合作的classes,可以一起運作。
配接器按功能可以分為如下3類:
可以改變函數(shù)或仿函數(shù)接口的適配器,稱為仿函數(shù)適配器;
針對容器的適配器,稱為容器適配器;
針對迭代器的適配器,稱為迭代器適配器。
本博客只介紹仿函數(shù)適配器,在實際編程中比較常見。
二、什么是可配接對象
什么是可配接對象?看到這句話可能還云里霧里的,真不太明白,下面通過一個很簡單的給數(shù)組排序的例子來解釋一下。
#include <iostream> #include <vector> #include <algorithm> #include <iterator> //ostream_iterator using namespace std; struct myLess { bool operator()(int lhs, int rhs) const { return lhs < rhs; } }; int main() { int IntArray[] = {7,4,2,9,1}; sort(IntArray, IntArray + sizeof(IntArray) / sizeof(int), myLess()); copy(IntArray, IntArray + sizeof(IntArray) / sizeof(int), ostream_iterator<int>(cout, "\n")); return 0; }
#程序執(zhí)行結果 [root@oracle Documents]# ./a.out 1 2 4 7 9
可以看到這個程序正確執(zhí)行了,現(xiàn)在我想讓程序內的數(shù)組進行降序。當然你可以重新定義一個仿函數(shù),但是我想用一個更快捷的方法,那就是not2函數(shù)。
//修改排序那一行的函數(shù) sort(IntArray, IntArray + sizeof(IntArray) / sizeof(int), not2(myLess()));
但是我發(fā)現(xiàn)這樣是編譯不過的,為什么呢?這就回到我們的主題了,因為myLess不是一個可配接對象。那么如何讓它變成一個可配接對象呢,繼續(xù)往下看。
三、unary_function和binary_function
為什么剛剛寫的myLess對象是不可配接的呢?因為它缺少argument_type、first_argument_type、second_argument_type和result_type這些特殊類型的定義。而unary_function和binary_function則可以提供這些類型的定義。我們在定義仿函數(shù)的時候,只需繼承自這2個函數(shù),那么我們的仿函數(shù)就是可配接的對象了。由于unary_function和binary_function是STL提供的模版,所以必須要指定必要的參數(shù)類型。
#include <iostream> #include <vector> #include <algorithm> #include <iterator> //ostream_iterator #include <functional> //binary_function, not2 using namespace std; //第一個參數(shù),第二個參數(shù),返回值 struct myLess : public binary_function<int, int, bool> { bool operator()(int lhs, int rhs) const { return lhs < rhs; } }; int main() { int IntArray[] = {7,4,2,9,1}; sort(IntArray, IntArray + sizeof(IntArray) / sizeof(int), not2(myLess())); copy(IntArray, IntArray + sizeof(IntArray) / sizeof(int), ostream_iterator<int>(cout, "\n")); return 0; }
#程序執(zhí)行結果 [root@oracle Documents]# ./a.out 9 7 4 2 1
傳遞給unary_function和binary_function的模版參數(shù)正是函數(shù)子類的operator()的參數(shù)類型和返回值。如果operator()接受一個參數(shù),則使用unary_function<參數(shù), 返回值>;如果operator()接受兩個參數(shù),則使用binary_function<參數(shù)1, 參數(shù)2, 返回值>。
一般情況下,傳遞給unary_function和binary_function的非指針類型需要去掉const和引用(&)部分。如下:
struct myLess : public binary_function<myClass, myClass, bool> { bool operator()(const myClass &lhs, const myClass &rhs) const { return lhs < rhs; } };
但是以指針作為參數(shù)或返回值的函數(shù)子類,一般規(guī)則是,傳給unary_function和binary_function的類型與operator()的參數(shù)和返回類型完全相同。如下:
struct myLess : public binary_function<const myClass *, const myClass *, bool> { bool operator()(const myClass *lhs, const myClass *rhs) const { return lhs < rhs; } };
四、標準的函數(shù)配接器
1. not1和not2
這2個配接器都是對可配接對象的否定。上面已經介紹過使用方法了。那么什么時候用not1,什么時候用not2呢?
如果可配接對象的operator()接受一個參數(shù)則使用not1;如果可配接對象的operator()接受兩個參數(shù)則使用not2。
2. bind1st和bind2nd
bind1st表示我們綁定第一個參數(shù),bind2st表示我們綁定第二個參數(shù)。
#include <iostream> #include <vector> #include <algorithm> #include <iterator> //ostream_iterator #include <functional> //binary_function, bind1st using namespace std; struct myLess : public binary_function<int, int, bool> { bool operator()(int lhs, int rhs) const { return lhs < rhs; } }; int main() { int IntArray[] = {7,4,2,9,1}; vector<int> IntVec(IntArray, IntArray + sizeof(IntArray) / sizeof(int)); IntVec.erase(remove_if(IntVec.begin(), IntVec.end(), bind1st(myLess(), 5)), IntVec.end()); copy(IntVec.begin(), IntVec.end(), ostream_iterator<int>(cout, "\n")); return 0; }
#程序執(zhí)行結果 [root@oracle Documents]# ./a.out 4 2 1
bind1st(myLess(), 5)相當于把5賦值給lhs,那么表達式就變成 5 < rhs,所以7和9就被刪除了。
如果把bind1st(myLess(), 5)改成bind2nd(myLess(), 5)),就相當于把5賦值給rhs,那么表達式就變成 lhs < 5, 所以1、2和4就被刪除了。bind1st(myLess(), 5) 等于 not1(bind2nd(myLess(), 5)))。
五、ptr_fun、mem_fun和mem_fun_ref
我們已經知道仿函數(shù)通過繼承unary_function和binary_function可以變成可配接對象,那么普通函數(shù)或者類的成員函數(shù)如何變成可配接對象呢?這就需要用到標題中的三個函數(shù)了。
#include <iostream> #include <vector> #include <algorithm> #include <iterator> //ostream_iterator #include <functional> //not2 using namespace std; class sortObj { public: bool memComp(const sortObj *other) { return *this < *other; } bool memComp_const(const sortObj &other) const { return *this < other; } public: sortObj(int v) : value(v){} ~sortObj(){} friend bool operator<(const sortObj &lhs, const sortObj &rhs) { return lhs.value < rhs.value; } friend ostream & operator<<(ostream &os, const sortObj &obj) { return os << obj.value << endl; } private: int value; }; bool sortFun(const sortObj &lhs, const sortObj &rhs) { return lhs < rhs; } //把指針轉換成對象 sortObj & ptrToObj(sortObj *ptr) { return *ptr; } int main() { sortObj objArray[] = { sortObj(7), sortObj(4), sortObj(2), sortObj(9), sortObj(1) }; vector<sortObj> objVec(objArray, objArray + sizeof(objArray) / sizeof(sortObj)); //配接普通函數(shù)(降序) sort(objVec.begin(), objVec.end(), not2(ptr_fun(sortFun))); copy(objVec.begin(), objVec.end(), ostream_iterator<sortObj>(cout, "")); cout << endl; srand(time(NULL)); random_shuffle(objVec.begin(), objVec.end()); //打亂順序 //配接對象的成員函數(shù)(升序) sort(objVec.begin(), objVec.end(), mem_fun_ref(&sortObj::memComp_const)); copy(objVec.begin(), objVec.end(), ostream_iterator<sortObj>(cout, "")); cout << endl; //配接指針的成員函數(shù)(降序) vector<sortObj *> objVecPtr; objVecPtr.push_back(new sortObj(7)); //內存泄漏了,不要在意這些細節(jié) objVecPtr.push_back(new sortObj(4)); objVecPtr.push_back(new sortObj(2)); objVecPtr.push_back(new sortObj(9)); objVecPtr.push_back(new sortObj(1)); sort(objVecPtr.begin(), objVecPtr.end(), not2(mem_fun(&sortObj::memComp))); transform(objVecPtr.begin(), objVecPtr.end(), ostream_iterator<sortObj>(cout, ""), ptrToObj); return 0; }
上述代碼中,首先調用not2(ptr_fun(sortFun)),用ptr_fun對普通函數(shù)sortFun進行配接;其次調用mem_fun_ref(&sortObj::memComp_const)和not2(mem_fun(&sortObj::memComp))對sortObj類的成員函數(shù)進行配接。這里有的童鞋可能有疑問:memComp明明只有一個形參,為什么用not2而不是not1?成員函數(shù)在別調用的時候,會自動傳進this指針的,所以這里還是兩個參數(shù)。
mem_fun和mem_fun_ref都是對類的成員函數(shù)進行配接,那么它們有什么區(qū)別嗎?相信細心的童鞋已經看出來了,當容器中存放的是對象實體的時候用mem_fun_ref,當容器中存放的是對象的指針的時候用mem_fun。
mem_fun和mem_fun_ref有一個很大的弊端:它們只能接收0個或1個參數(shù)(不算this指針)。這個實在有點局限。新的bind函數(shù)模板可以用于任何函數(shù)、函數(shù)指針、成員函數(shù)、函數(shù)對象、模板函數(shù)、lambda表達式,還可以嵌套bind。
六、bind
上面介紹的這些配接器都是C++11之前使用的,在C++11中這些配接器已經被廢棄了,改成使用bind函數(shù)。如果想在C++11之前的版本中使用這個函數(shù),有Linux下有兩種方法。
1. 包含<functional>頭文件,在編譯的時候增加編譯參數(shù)-std=c++0x,那么就可以使用std::bind了
2. 包含#include <tr1/functional> 頭文件,直接可以使用std::tr1::bind了。
在bind中有2種方式可以把值傳遞進bind函數(shù)中,一種是預先綁定的參數(shù),這個參數(shù)是通過pass-by-value傳遞進去的;另一種是不預先綁定的參數(shù),這種參數(shù)是通過placeholders占位符傳遞進去的,它是pass-by-reference的。
上述代碼中的3個排序函數(shù)配接器可以替換成下面這樣的bind,如下:
//綁定普通函數(shù)(降序) sort(objVec.begin(), objVec.end(), tr1::bind(sortFun, tr1::placeholders::_2, tr1::placeholders::_1)); //綁定類對象的成員函數(shù)(升序) sort(objVec.begin(), objVec.end(), tr1::bind(&sortObj::memComp_const, tr1::placeholders::_1, tr1::placeholders::_2)); //綁定類指針的成員函數(shù)(降序) sort(objVecPtr.begin(), objVecPtr.end(), tr1::bind(&sortObj::memComp, tr1::placeholders::_2, tr1::placeholders::_1));
注意,在上面的例子中,我使用了not2方法對結果進行倒序。但是bind和not2是不兼容的,實現(xiàn)倒序的方法也很簡單,先傳遞placeholders::_2,再傳遞placeholders::_1就可以實現(xiàn)了。
bind的其他用法實例:
#include <iostream> #include <tr1/memory> #include <tr1/functional> using namespace std; using namespace std::tr1; int main() { //嵌套bind //(x + y) * x function<int (int, int)> func = bind(multiplies<int>(), bind(plus<int>(), placeholders::_1, placeholders::_2), placeholders::_1); cout << func(2, 3) << endl; //reference_wrapper<T>類型, 實現(xiàn)綁定引用 int x = 10; function<int ()> funcMinus = bind(minus<int>(), 100, cref(x)); cout << funcMinus() << endl; //輸出90 x = 50; cout << funcMinus() << endl; //輸出50 return 0; }
function的<>中定義的是綁定后的函數(shù)的原型,即func和funcMinus的函數(shù)原型
#程序執(zhí)行結果 [root@oracle Documents]# ./a.out 10 90 50
七、內置仿函數(shù)
上面介紹bind的例子中已經使用過C++內置的仿函數(shù),這里再進行一下匯總
1)算術類仿函數(shù)
加:plus<T> 接收2個參數(shù)
減:minus<T> 接收2個參數(shù)
乘:multiplies<T> 接收2個參數(shù)
除:divides<T> 接收2個參數(shù)
模?。簃odulus<T> 接收2個參數(shù)
否定:negate<T> 接收1個參數(shù)(正數(shù)變負數(shù),負數(shù)變正數(shù))
2)關系運算類仿函數(shù)
等于:equal_to<T> 接收2個參數(shù)
不等于:not_equal_to<T> 接收2個參數(shù)
大于:greater<T> 接收2個參數(shù)
大于等于:greater_equal<T> 接收2個參數(shù)
小于:less<T> 接收2個參數(shù)
小于等于:less_equal<T> 接收2個參數(shù)
3)邏輯運算仿函數(shù)
邏輯與:logical_and<T> 接收2個參數(shù)
邏輯或:logical_or<T> 接收2個參數(shù)
邏輯否:logical_no<T> 接收2個參數(shù)
另外有需要云服務器可以了解下創(chuàng)新互聯(lián)scvps.cn,海內外云服務器15元起步,三天無理由+7*72小時售后在線,公司持有idc許可證,提供“云服務器、裸金屬服務器、高防服務器、香港服務器、美國服務器、虛擬主機、免備案服務器”等云主機租用服務以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡單易用、服務可用性高、性價比高”等特點與優(yōu)勢,專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應用場景需求。
新聞標題:C++函數(shù)配接器-創(chuàng)新互聯(lián)
鏈接URL:http://aaarwkj.com/article46/geghg.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供虛擬主機、小程序開發(fā)、軟件開發(fā)、云服務器、品牌網站設計、外貿網站建設
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)