Cocos2d-x v3.0 新的事件排程方法 lambda表示式的使用
歡迎加入 Cocos2d-x 交流群: 193411763
轉載請註明原文出處:http://blog.csdn.net/u012945598/article/details/24603251
Cocos 2d-x 3.0 版本中引入了C++ 11的特性。其中就包含了回撥函中使用Lambda物件。
下面我們來看一段TestCpp中的程式碼:
在上圖的觸控事件的回撥函式中,共使用了三次Lambda表示式:
[ ](Touch * touch,Event * event){ };
下面我們就來介紹一下Lambda表示式的使用方法。
正常情況下,如果我們需要在很多地方使用相同的操作,通常應該定義一個函式來實現這個功能。
而有些時候,我們只需要在一兩個地方使用到一些簡單的操作,而又不想去定義這個函式名,那麼此時便可以Lambda表示式來實現我們的功能。
一個完整的lambda表示式的表達形式如下:
[capture list](parameter list)->return type (function body)
[捕獲列表] (引數列表) ->返回型別 (函式體)
那麼為什麼圖中的Lambda表示式的形式與上述的形式不一樣呢?
原因是 Lambda表示式的引數列表和返回型別是可以忽略的,但是捕獲列表和函式體一定要包含。
也就是說,Lambda表示式實際上就是一個匿名函式,它的優點與行內函數(又稱內嵌函式、內建函式)類似,但Lambda表示式可能會定義在函式內部。
那麼行內函數的優點是什麼呢?我們舉個例子來說明,比如我們的程式中有這樣一段程式碼:
void A(){
....//函式體略
}
void main(){
.... //省略
A( );//呼叫A函式
.... //省略
}
上述程式碼執行的主要過程如下:
1.主調函式main執行完呼叫A函式前的語句後,在轉去呼叫A函式前,首先需要記錄當前執行的指令地址,也就是做一個“保護現場”的操作,用於執行完A函式後繼續執行後續程式碼。
2.然後流程的控制會被轉移到A函式的入口,並且執行A函式中的函式體內的語句.
3.執行完成後,流程才會返回到之前記錄的地址處,並且根據之前所記錄的資訊做"恢復現場"操作,保證程式正常執行。
上述過程的每一個操作都需要花費一定的時間,如果A函式需要被頻繁的使用,那麼我們花費的時間就會很長,從而造成效率降低。
為了解決這個問題,C++為我們提供的了行內函數,所謂行內函數,就是通過將一個函式宣告為inline function,從而達到在編譯的過程中,直接將所呼叫的函式的函式體部分直接拷貝到主調函式,而不需要將流程轉到這個函式中去,以此來減少程式的執行時間。
這是因為當一個函式的函式體規模很小的時候,函式呼叫過程中的時間開銷會超過執行函式所需要的時間。
這就是使用行內函數的好處,而對於Lambda表示式,我們可以將它理解為一個未命名的行內函數。
下面我們對lambda表示式的形式進行逐一分析:
1.“ [捕獲列表] ”
首先我們觀察一下上圖中的第一個lambda表示式與第三個lambda表示式的捕獲列表部分的區別。
可以看到,上圖的第一個表示式中捕獲列表為空 [ ],而第三個表示式中的捕獲列表中包含了一個等號 [=]。
下面我們再觀察一下上圖中第一個與第三個lambda表示式的函式體內都使用到了哪些變數。
可以看到,第一個表示式中所有的變數,均是在Lambda表示式中定義的(log除外,因為log函式包含在標頭檔案中),
而在第三個表示式中所使用到的sprite1,sprite2等變數,並不是在lambda表示式中定義的,而是當前函式中或是當前類中的變數。
那麼我們就可以總結出,在Lambda表示式的函式體內,是不能夠訪問到外部的變數的,如果想要使用函式體外定義的變數,就需要將它們進行"捕獲",上圖第三個lambda表示式採用的正是“值捕獲”,與它對應的另外一種為“引用捕獲”。
[ ]:空捕獲列表,即lambda表示式不能夠使用所在函式中的變數
[=]:值捕獲,即lambda表示式可以以拷貝的方式訪問到函式中變數的值
[&]:引用捕獲,即lambda表示式中所使用的其所在函式中的變數均是引用方式
當我們不希望在捕獲的時候將所有的變數都捕獲的時候,我們可以使用如下的方式進行捕獲,例如:
[=sprite1,&sprite2]
這裡我們僅僅捕獲了兩個變數,第一個變數是以值拷貝的方式捕獲,第二個是以引用方式捕獲,變數與變數之間用逗號分隔。
正常情況下,如果一個變數是值拷貝,Lambda不能改變它的值,如果我們希望改變一個值拷貝的變數的值,就需要在引數列表前加上關鍵字mutable
例如:
auto s1=10;
auto s2=[=s1](){return ++s1};//錯誤,因為s1是值拷貝,不能改變s1的值
auto s2=[=s1]() mutable {return ++s1};//正確
2.(引數列表)
Lambda表示式傳遞引數時需要注意的是,Lambda表示式不能有預設引數,也就是說Lambda表示式的實引數與形引數必須相等。
其他情況Lambda表示式的引數部分與普通函式並無區別,一般會結合STL使用。
例如:
void Test(){
vector<int> myVec; //建立一個int 型別容器
myVec.push_back(1); //插入資料 1
myVec.push_back(2);//插入資料 2
int a=10; //建立區域性變數 a
for_each(myVec.begin(),myVec.end(),[&](int v)mutable(cout<<v+a<<endl;a++)); //將容器中元素作為引數傳到lambda表示式 輸出a+v結果為 11 13
cout<<a<<endl; //輸出a 結果為12
}
3.->return type
之間我們已經提到,Lambda的返回值是可以省略的。
原因是編譯器會根據return的型別來推導返回值,但是如果需要return後再做一個型別轉換,我們就可以通過寫一個返回型別來完成。
例如:cout<<[](float f){return f}(1.5); //這裡我們將1.5作為引數傳入並列印,返回結果就是實參的值1.5
cout<<[](float f)->int{return f}(1.5); //我們將返回值強制轉換為int 輸出結果為1
4.函式體
函式體部分與普通函式並無區別,我們只需要注意以上幾點,在函式體部分就不會出現問題。
現在再回頭看看TestCpp中的觸控事件,我們就可以明白其中的道理了。