C++11學(xué)習(xí)之包裝器解析
概念
function包裝器 也叫作適配器。C++中的function本質(zhì)是一個(gè)類模板,也是一個(gè)包裝器。
那么我們來(lái)看看,我們?yōu)槭裁葱枰猣unction呢?
包裝器定義式:
// 類模板原型如下 template <class T> function; // undefined template <class Ret, class... Args> class function<Ret(Args...)>;
模板參數(shù)說(shuō)明:
- Ret: 是被包裝的可調(diào)用對(duì)象的返回值類型
- Args... :是被包裝的可調(diào)用對(duì)象的形參類型
function包裝器可以對(duì)可調(diào)用對(duì)象進(jìn)行包裝,包括函數(shù)指針、函數(shù)名、仿函數(shù)(函數(shù)對(duì)象)、lambda表達(dá)式
int f(int a, int b) { return a + b; } struct Functor { public: int operator() (int a, int b) { return a + b; } }; class Plus { public: //靜態(tài) vs 非靜態(tài) static int plusi(int a, int b) { return a + b; } double plusd(double a, double b) { return a + b; } }; int main() { function<int(int, int)> f1 = f; f1(1, 2); function<int(int, int)> f2 = Functor(); f2(1, 2); function<int(int, int)> f3 = Plus::plusi; f3(1, 2); //非靜態(tài)成員函數(shù) 要 + 對(duì)象 function<double(Plus, double, double)> f4 = &Plus::plusd; f4(Plus(), 1.1, 2.2); return 0; }
注意事項(xiàng):
取靜態(tài)成員函數(shù)的地址可以不用取地址運(yùn)算符“&”,但取非靜態(tài)成員函數(shù)的地址必須使用取地址運(yùn)算符“&”
非靜態(tài)成員函數(shù)在調(diào)用的時(shí)候要 + 對(duì)象,因?yàn)榉庆o態(tài)成員函數(shù)的第一個(gè)參數(shù)是隱藏this指針,所以在包裝時(shí)需要指明第一個(gè)形參的類型為類的類型。
類型統(tǒng)一
對(duì)于以下函數(shù)模板useF:
- 傳入該函數(shù)模板的第一個(gè)參數(shù)可以是任意的可調(diào)用對(duì)象,比如函數(shù)指針、仿函數(shù)、lambda表達(dá)式等
- useF中定義了靜態(tài)變量count,并在每次調(diào)用時(shí)將count的值和地址進(jìn)行了打印,可判斷多次調(diào)用時(shí)調(diào)用的是否是同一個(gè)useF函數(shù)。
代碼如下:
template<class F, class T> T useF(F f, T x) { static int count = 0; cout << "count:" << ++count << endl; cout << "count:" << &count << endl; return f(x); }
在不使用包裝器,直接傳入對(duì)象的時(shí)候,會(huì)實(shí)例化出三份
double f(double i) { return i / 2; } struct Functor { double operator()(double d) { return d / 3; } }; int main() { //函數(shù)指針 cout << useF(f, 11.11) << endl; //仿函數(shù) cout << useF(Functor(), 11.11) << endl; //lambda表達(dá)式 cout << useF([](double d)->double{return d / 4; }, 11.11) << endl; return 0; }
輸出結(jié)果如下:
由于函數(shù)指針、仿函數(shù)、lambda表達(dá)式是不同的類型,因此useF函數(shù)會(huì)被實(shí)例化出三份,三次調(diào)用useF函數(shù)所打印count的地址也是不同的。
- 但實(shí)際這里其實(shí)沒(méi)有必要實(shí)例化出三份useF函數(shù),因?yàn)槿握{(diào)用useF函數(shù)時(shí)傳入的可調(diào)用對(duì)象雖然是不同類型的,但這三個(gè)可調(diào)用對(duì)象的返回值和形參類型都是相同的
- 這時(shí)就可以用包裝器分別對(duì)著三個(gè)可調(diào)用對(duì)象進(jìn)行包裝,然后再用這三個(gè)包裝后的可調(diào)用對(duì)象來(lái)調(diào)用useF函數(shù),這時(shí)就只會(huì)實(shí)例化出一份useF函數(shù)
- 根本原因就是因?yàn)榘b后,這三個(gè)可調(diào)用對(duì)象都是相同的function類型,因此最終只會(huì)實(shí)例化出一份useF函數(shù),該函數(shù)的第一個(gè)模板參數(shù)的類型就是function類型的
包裝后代碼如下:
int main() { // 函數(shù)指針 function<double(double)> f1 = f; cout << useF(f1, 11.11) << endl; // 函數(shù)對(duì)象 function<double(double)> f2 = Functor(); cout << useF(f2, 11.11) << endl; // lamber表達(dá)式 function<double(double)> f3 = [](double d)->double { return d / 4; }; cout << useF(f3, 11.11) << endl; return 0; }
例題:求解逆波蘭表達(dá)式
題目:
解題思路:
- 首先定義一個(gè)棧,依次遍歷所給字符串
- 遇到數(shù)字,直接入棧,遇到操作符,則從棧定拋出兩個(gè)數(shù)字進(jìn)行對(duì)應(yīng)的運(yùn)算,并將運(yùn)算后得到的結(jié)果壓入棧中
- 所給字符串遍歷完畢后,棧頂?shù)臄?shù)字就是逆波蘭表達(dá)式的計(jì)算結(jié)果
此處的包裝器:
- 建立各個(gè)運(yùn)算符與其對(duì)應(yīng)需要執(zhí)行的函數(shù)之間的映射關(guān)系,當(dāng)需要執(zhí)行某一運(yùn)算時(shí)就可以直接通過(guò)運(yùn)算符找到對(duì)應(yīng)的函數(shù)進(jìn)行執(zhí)行
- 當(dāng)運(yùn)算類型增加時(shí),就只需要建立新增運(yùn)算符與其對(duì)應(yīng)函數(shù)之間的映射關(guān)系即可(其他代碼不用動(dòng))
class Solution { public: int evalRPN(vector<string>& tokens) { stack<long long> st; map<string, function<long long(long long, long long)>> opfuncMap = { //自動(dòng)構(gòu)造pair ~ 初始化列表構(gòu)造 {"+", [](long long a , long long b){return a + b;}}, {"-", [](long long a , long long b){return a - b;}}, {"*", [](long long a , long long b){return a * b;}}, {"/", [](long long a , long long b){return a / b;}}, }; for(auto& str : tokens) { if(opfuncMap.count(str)) { //操作符 :出棧(先出右,再出左) long long right = st.top(); st.pop(); long long left = st.top(); st.pop(); st.push(opfuncMap[str](left, right)); } else { //操作數(shù):入棧 st.push(stoll(str)); } } return st.top(); } };
包裝器的意義
將可調(diào)用對(duì)象的類型進(jìn)行統(tǒng)一,便于我們對(duì)其進(jìn)行統(tǒng)一化管理。
包裝后明確了可調(diào)用對(duì)象的返回值和形參類型,更加方便使用者使用。
bind 包裝器
bind 是一種函數(shù)包裝器,也叫做適配器。它可以接受一個(gè)可調(diào)用對(duì)象,生成一個(gè)新的可調(diào)用對(duì)象來(lái)“適應(yīng)”原對(duì)象的參數(shù)列表,C++ 中的 bind 本質(zhì)還是一個(gè)函數(shù)模板
bind函數(shù)模板的原型如下:
template <class Fn, class... Args> /* unspecified */ bind(Fn&& fn, Args&&... args); template <class Ret, class Fn, class... Args> /* unspecified */ bind(Fn&& fn, Args&&... args);
模板參數(shù)說(shuō)明:
- fn:可調(diào)用對(duì)象
- args...:要綁定的參數(shù)列表:值或占位符
調(diào)用bind的一般形式
auto newCallable = bind(callable, arg_list);
callable:需要包裝的可調(diào)用對(duì)象
newCallable:生成的新的可調(diào)用對(duì)象
arg_list:逗號(hào)分隔的參數(shù)列表,對(duì)應(yīng)給定的 callable 的參數(shù),當(dāng)調(diào)用 newCallable時(shí),newCallable 會(huì)調(diào)用 callable,并傳給它 arg_list 中的參數(shù)
_1 _2 ... 是定義在placeholders命名空間中,代表綁定函數(shù)對(duì)象的形參;_1代表第一個(gè)形參,_2代表第二個(gè)形參 …
舉例:
using namespace placeholders; int x = 2, y = 10; Div(x, y); auto bindFun1 = bind(Div, _1, _2);
bind 綁定固定參數(shù)
原本傳入的參數(shù)要求是要3個(gè),現(xiàn)在只需要輸入兩個(gè)參數(shù),因?yàn)榻壎斯潭ǖ暮瘮?shù)對(duì)象
using namespace placeholders; class Sub { public: int sub(int a, int b) { return a - b; } }; int main() { //function<int(Sub, int, int)> fsub = &Sub::sub; function<int(int, int)> fsub = bind(&Sub::sub, Sub(), _1, _2); }
想把Mul函數(shù)的第三個(gè)參數(shù)固定綁定為1.5,可以在綁定時(shí)將參數(shù)列表的placeholders::_3設(shè)置為1.5。比如:
int Mul(int a, int b, int rate) { return a * b * rate; } int main() { function<int(int, int)> fmul = bind(Mul, _1, _2, 1.5); }
調(diào)整傳參順序
對(duì)于 Sub 類中的 sub 函數(shù),因?yàn)?sub 的第一個(gè)參數(shù)是隱藏的 this 指針,如果想要在調(diào)用 sub 時(shí)不用對(duì)象進(jìn)行調(diào)用,那么可以將 sub 的第一個(gè)參數(shù)固定綁定為一個(gè) Sub 對(duì)象:
using namespace placeholders; class Sub { public: int sub(int a, int b) { return a - b; } }; int main() { //綁定固定參數(shù) function<int(int, int)> func = bind(&Sub::sub, Sub(), _1, _2); cout << func(1, 2) << endl; return 0; }
此時(shí)調(diào)用對(duì)象時(shí)就只需要傳入用于相減的兩個(gè)參數(shù),因?yàn)樵谡{(diào)用時(shí)會(huì)固定幫我們傳入一個(gè)匿名對(duì)象給 this 指針。
如果想要將 sub 的兩個(gè)參數(shù)順序交換,那么直接在綁定時(shí)將 _1 和_2 的位置交換一下就行了:
using namespace placeholders; class Sub { public: int sub(int a, int b) { return a - b; } }; int main() { //綁定固定參數(shù) function<int(int, int)> func = bind(&Sub::sub, Sub(), _2, _1); cout << func(1, 2) << endl; return 0; }
其原理:第一個(gè)參數(shù)會(huì)傳給_1,第二個(gè)參數(shù)會(huì)傳給 _2,因此可以在綁定時(shí)通過(guò)控制 _n 的位置,來(lái)控制第 n 個(gè)參數(shù)的傳遞位置
bind包裝器的意義
1.將一個(gè)函數(shù)的某些參數(shù)綁定為固定的值,讓我們?cè)谡{(diào)用時(shí)可以不用傳遞某些參數(shù)。
2.可以對(duì)函數(shù)參數(shù)的順序進(jìn)行靈活調(diào)整。
以上就是C++11學(xué)習(xí)之包裝器解析的詳細(xì)內(nèi)容,更多關(guān)于C++11包裝器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言使用scanf連續(xù)輸入字符串出現(xiàn)的問(wèn)題
這篇文章主要介紹了C語(yǔ)言使用scanf連續(xù)輸入字符串出現(xiàn)的問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12C語(yǔ)言基礎(chǔ)函數(shù)用法示例詳細(xì)解析
最接地氣的C函數(shù)基礎(chǔ)介紹,此處對(duì)于函數(shù)的相關(guān)知識(shí)點(diǎn)做一些簡(jiǎn)要的介紹,作者實(shí)屬初學(xué),寫博客也是作者學(xué)習(xí)的一個(gè)過(guò)程,難免文章中有內(nèi)容理解不到位或者有不當(dāng)之處,還請(qǐng)朋友們不吝指正2021-11-11QT Creator+OpenCV實(shí)現(xiàn)圖像灰度化的示例代碼
這篇文章主要為大家詳細(xì)介紹了QT如何利用Creator和OpenCV實(shí)現(xiàn)圖像灰度化效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以嘗試一下2022-12-12C語(yǔ)言實(shí)現(xiàn)三子棋游戲的示例代碼
今天我們將會(huì)用C語(yǔ)言實(shí)現(xiàn)三子棋。所謂三子棋,就是三行三列的棋盤,玩家可以和電腦下棋,率先連成三個(gè)的獲勝。話不多說(shuō),我們開(kāi)始吧2022-10-10詳解vs2022創(chuàng)建及調(diào)用.lib的方法
這篇文章主要介紹了vs2022創(chuàng)建及調(diào)用.lib的方法,調(diào)用Lib的原則就是可以讓編譯器找到頭文件和庫(kù)文件的目錄,并正確引入,本文給大家詳細(xì)講解需要的朋友可以參考下2022-11-11C++?Boost?CircularBuffer算法超詳細(xì)精講
Boost是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱。Boost庫(kù)是一個(gè)可移植、提供源代碼的C++庫(kù),作為標(biāo)準(zhǔn)庫(kù)的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開(kāi)發(fā)引擎之一,是為C++語(yǔ)言標(biāo)準(zhǔn)庫(kù)提供擴(kuò)展的一些C++程序庫(kù)的總稱2022-11-11