函數(shù)對象是C++中以參數(shù)形式傳遞函數(shù)的一個很好的方法,我們將函數(shù)包裝成類,并且利用()運算符重載實現(xiàn)。
1 2 3 4 5 6 | typedef class hello { public : void operator()( double x) { cout << x << endl; } } hello; |
這時候hello是一個類,我們可以實例化一個對象hello h;,然后通過h(3.14)的方式來調用這個類的成員函數(shù),如果某個函數(shù)需要這個函數(shù)作為回調函數(shù),則可以將這個hello類的對象傳入即可。
因為這是一個類的定義,因此我們完全可以在其中定義一些包含額外信息的成員和一些構造函數(shù),讓這個函數(shù)對象可以做更多不同的可定制的任務,最終的行為實際上只是調用了這個()運算符重載函數(shù)。這種做法比C++函數(shù)指針要容易理解得多,也不容易寫錯。
而Lambda表達式則是C++中的新語法,實現(xiàn)了許多程序員渴望的部分閉包特性。C++中Lambda表達式可以被視為一種匿名函數(shù),這樣,對于一些非常短,而且不太可能被其他地方的復用的小函數(shù),可以通過Lambda表達式提高代碼的可讀性。
在Lambda表達式中對于變量生命期的控制還是與完全支持閉包的JavaScript非常不同,總而言之,C++對于變量聲明期的控制在新標準中完全向前兼容,也就是局部變量一定在退出代碼塊時被銷毀,而不是觀察其是否被引用。因此,盡管C++的Lambda表達式中允許引用其代碼上下文中的值,但是實際上并不能夠保證引用的對象一定沒有被銷毀。
Lambda表達式對于上下文變量的引用有值傳遞和引用傳遞兩種方式,實際上,無論是哪種方式,在產(chǎn)生Lambda表達式對象時,這些上下文值就已經(jīng)從屬于Lambda表達式對象了,也就是說,代碼運行至定義Lambda表達式處時,通過值傳遞方式訪問的上下文變量值已經(jīng)被寫入Lambda表達式的棧中,而引用方式傳遞的上下文變量地址被寫入Lambda表達式的棧中。因此,調用Lambda表達式時得到的上下文變量值就是定義Lambda表達式時這些變量的值,而引用的上下文變量,如果已經(jīng)被銷毀,則會出現(xiàn)運行時異常。
Lambda表達式的基本語法是:
[上下文變量說明](Lambda表達式參數(shù)表) -> 返回類型 { 語句塊 }
上下文變量說明部分就是說明對于上下文變量的引用方式,=表示值傳遞,&表示引用傳遞,例如,&s就表示s變量采用引用傳遞,不同的說明項之間用逗號分隔,可以為空,但是方括號不能夠省略。第一項可以是單獨的一個=或者&,表示,所有上下文變量若無特殊說明一律采用值傳遞/引用傳遞,什么都不寫默認為值傳遞。
Lambda表達式和TR1標準對應的function<返回類型 (參數(shù)表)>對象是可以互相類型轉換的,這樣,我們也可以將Lambda表達式作為參數(shù)進行傳遞,也可以作為返回值返回。
下面看一個Lambda表達式各種使用方法的完整例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | // compile with: /EHsc #include <iostream> #include <string> #include <functional> //這是TR1的頭文件,定義了function類模板 using namespace std; typedef class hello { public : void operator()( double x) { cout << x << endl; } } hello; //函數(shù)對象的定義,也是非常常用的回調函數(shù)實現(xiàn)方法 void callhello(string s, hello func) { cout << s; func(3.14); } //一個普通的函數(shù) void callhello(string s, const function< void ( double x)>& func) { cout << s; func(3.14); } //這個函數(shù)會接受一個字符串和一個Lambda表達式作為參數(shù) void callhello(string s, double d) { [=] ( double x) { cout << s << x << endl; }(d); } //這個函數(shù)體內(nèi)定義了一個Lambda表達式并立即調用 function< void ( double x)> returnLambda(string s) { cout << s << endl; function< void ( double x)> f = ([= /*這里必須使用值傳遞,因為s變量在returnLambda返回后就被銷毀*/ ] ( double x) { cout << s << x << endl; }); s = "changed" ; //這里對s的修改Lambda表達式是無法感知的,調用這句語句前s在Lambda表達式中的值已經(jīng)確定了 return f; } //這個函數(shù)接受了一個值傳遞的字符串變量s,我們將Lambda表達式作為返回值返回 function< void ( double x)> returnLambda2(string& s) { cout << s << endl; function< void ( double x)> f = ([&s /*這里可以使用引用傳遞,因為s是引用方式傳入的,不隨函數(shù)返回而消亡*/ ] ( double x) { cout << s << x << endl; }); s = "changed" ; //這里對s的修改Lambda表達式是可以感知的,因為s以引用方式參與到Lambda表達式上下文中 return f; } //這個函數(shù)接受了一個引用傳遞的字符串變量s,將Lambda表達式作為返回值返回 int main() { hello h; callhello( "hello:" , h); //用函數(shù)對象的方式實現(xiàn)功能 callhello( "hello lambda:" , -3.14); //這個函數(shù)體內(nèi)定義了一個Lambda表達式并立即調用 int temp = 6; callhello( "hello lambda2:" , [&] ( double x) -> void { cout << x << endl; cout << temp++ << endl; }); //這個函數(shù)會接受一個字符串和一個Lambda表達式作為參數(shù) cout << temp << endl; function< void ( double x)> f = returnLambda( "lambda string" ); //這個函數(shù)接受了一個值傳遞的字符串變量s,我們將Lambda表達式作為返回值返回 f(3.3); string lambdastring2 = "lambda string2" ; //這個變量在main函數(shù)返回時才被銷毀 f = returnLambda2(lambdastring2); //這個函數(shù)接受了一個引用傳遞的字符串變量s,將Lambda表達式作為返回值返回 f(6.6); system ( "pause" ); } |
聯(lián)系客服