cocos new 出新的項目以後,仔細閱讀代碼,才發現了一句3.0區別於2.0的代碼:c++
auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
2.0內的代碼用的不是CC_CALLBACK_1而是menu_selector.c#
CC_CALLBACK系列是3.0基於c++11的特性新增的。CC_CALLBACK系列的定義以下:閉包
// new callbacks based on C++11 #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__) #define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__) #define CC_CALLBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__) #define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, ##__VA_ARGS__)
能夠看出,CC_CALL_BACK系統後的數字,表示函數指針的參數個數。明白了這一點,選擇CC_CALLBACK時,就不會出錯鳥。函數
而看示例代碼時,還會發現一個有意思的使用方法:this
listener->onTouchesBegan = CC_CALLBACK_2(Layer::onTouchesBegan, this);
此時不由要問onTouchesBegan又是啥,爲啥不能直接函數指針賦值呢?spa
看定義就能明白了指針
std::function<void(const std::vector<Touch*>&, Event*)> onTouchesBegan;
由於CC_CALLBACK系列是std::bind,而onTouchesBegan是std::function來定義的。那麼std::bind和std::function又有什麼區別呢?c++11
有博文說:code
function模板類和bind模板函數,使用它們能夠實現相似函數指針的功能,但卻比函數指針更加靈活,特別是函數指向類的非靜態成員函數時。orm
std::function能夠綁定到全局函數/類靜態成員函數(類靜態成員函數與全局函數沒有區別),若是要綁定到類的非靜態成員函數,則須要使用std::bind。
標準庫函數bind()和function()定義於頭文件<functional>中(該頭文件還包括許多其餘函數對象),用於處理函數及函數參數。
std::bind綁定器
bind()接受一個函數(或者函數對象,或者任何你能夠經過"(...)"符號調用的事物),生成一個其有某一個或多個函數參數被「綁定」或從新組織的函數對象。(譯註:顧名思義,bind()函數的意義就像它的函數名同樣,是用來綁定函數調用的某些參數的。)例如:
int f(int, char, double); auto ff = bind(f, _1, 'c', 1.2); // 綁定f()函數調用的第二個和第三個參數,返回一個新的函數對象爲ff,它只帶有一個int類型的參數 int x = ff(7); // f(7, 'c', 1.2);
參數的綁定一般稱爲"Currying"(譯註:Currying---「烹製咖喱燒菜」,此處意指對函數或函數對象進行加工修飾操做), "_1"是一個佔位符對象,用於表示當函數f經過函數ff進行調用時,函數ff的第一個參數在函數f的參數列表中的位置。第一個參數稱爲"_1", 第二個參數爲"_2",依此類推。例如:
int f(int, char, double); auto frev = bind(f, _3, _2, _1); // 翻轉參數順序 int x = frev(1.2, 'c', 7); // f(7, 'c', 1.2);
此處,auto關鍵字節約了咱們去推斷bind返回的結果類型的工做。
咱們沒法使用bind()綁定一個重載函數的參數,咱們必須顯式地指出須要綁定的重載函數的版本:
int g(int); double g(double); auto g1 = bind(g, _1); // 錯誤:調用哪個g() ? auto g2 = bind( (double(*)(double))g, _1); // 正確,可是至關醜陋
void H(int a); //綁定全局函數 auto f11 = std::bind(H, std::placeholders::_1); auto的類型其實是std::function<void(int)> //綁定帶參數的成員函數 std::function<void (char*, int)> f = std::bind(&ReadHandler::ConnectPreProcess, this, std::placeholders::_1, std::placeholders::_1); //三元函數轉換成一元函數 int f(int, char, double); // 綁定f()函數調用的第二個和第三個參數, // 返回一個新的函數對象爲ff,它只帶有一個int類型的參數 auto ff = bind(f, _1, ‘c’, 1.2); int x = ff(7);
本身寫代碼示例以下:
int Func(int x, int y); auto bf1 = std::bind(Func, 10, std::placeholders::_1); bf1(20); ///< same as Func(10, 20) int HelloWorld::AddFunc( int a, int b ) { return a + b; } bool HelloWorld::init() { auto bf2 = std::bind(&HelloWorld::AddFunc,this , std::placeholders::_1, std::placeholders::_2 ); auto result1 = bf2(10, 20); ///< same as a.Func(10, 20) std::function< int(int)> bf3 = std::bind(&HelloWorld::AddFunc, this, std::placeholders::_1, 100); auto result2 = bf3(10); ///< same as a.Func(10, 100) }
上面的例子中,bf1是把一個兩個參數普通函數的第一個參數綁定爲10,生成了一個新的一個參數的可調用實體體; bf2是把一個類成員函數綁定了類對象,生成了一個像普通函數同樣的新的可調用實體; bf3是把類成員函數綁定了類對象和第二個參數,生成了一個新的std::function對象。看懂了上面的例子,下面咱們來講說使用bind須要注意的一些事項:
std::function
它是函數、函數對象、函數指針、和成員函數的包裝器,能夠容納任何類型的函數對象,函數指針,引用函數,成員函數的指針。
以統一的方式處理函數、函數對象、函數指針、和成員函數。容許保存和延遲執行函數。
function是一個擁有任何能夠以"(...)"符號進行調用的值的類型。特別地,bind的返回結果能夠賦值給function類型。function十分易於使用。(譯註:更直觀地,能夠把function當作是一種表示函數的數據類型,就像函數對象同樣。只不過普通的數據類型表示的是數據,function表示的是函數這個抽象概念。)例如:
typedef std::function<float (int x, int y)> f ;// 構造一個函數對象,它能表示的是一個返回值爲float,兩個參數爲int,int的函數 struct int_div { // 構造一個能夠使用"()"進行調用的函數對象類型 float operator() (int x, int y) const { return ((float)x)/y; }; }; void HelloWorld::testing() { f f1= int_div(); // 賦值 auto result3 = f1( 10, 2); }
成員函數可被看作是帶有額外參數的自由函數:
struct int_div { // 構造一個能夠使用"()"進行調用的函數對象類型 float operator() (int x, int y) const { return ((float)x)/y; }; int int_div_fun( int x ){ return x; }; }; typedef std::function<int (int_div*, int)> f_2; bool HelloWorld::init() { f_2 f2 = std::mem_fn(&int_div::int_div_fun); // 指向成員函數 int_div int_div_object; int v = f2(&int_div_object, 5); // 在對象x上用參數5調用X::foo() std::function<int (int)> ff = std::bind( f2, &int_div_object, std::placeholders::_1); // f的第一個參數是&x v = ff(5); // 調用x.foo(5) }
ps:被vs2012的bug給坑了。由於看網上的代碼因而剛開始第9行是這麼寫的:f_2 f2 = &int_div::int_div_fun;
而後就報錯誤:Error 1 error C2664: 'std::_Func_class<_Ret,_V0_t,_V1_t>::_Set' : cannot convert parameter 1 from '_Myimpl *' to 'std::_Func_base<_Rx,_V0_t,_V1_t> *'
查了一下,vs2010沒有這個編譯錯誤,可是2012有。2012必須得加上std::mem_fn才能編譯。
struct int_div { // 構造一個能夠使用"()"進行調用的函數對象類型 float operator() (int x, int y) const { return ((float)x)/y; }; int int_div_fun( int x ){ return x; }; int_div( std::function<void()>& f ):m_callback(f){}; void Notify() { m_callback(); } std::function<void()> m_callback; };
void Foo(int x, std::function<void(int)>& f) { if(x%2==0) f(x); } void G(int x) { cout<<x<<endl; } void H(int x) { cout<<x+2<<endl; } void TestFoo() { auto f = std::bind(G, std::placeholders::_1); Foo(4, f); //在Foo函數外面更改f的行爲 f = std::bind(H, std::placeholders::_1); Foo(4, f); }
c++11中推出function是爲了泛化函數對象,函數指針,引用函數,成員函數的指針,讓咱們能夠按更統一的方式寫出更加泛化的代碼;推出bind是爲了替換和加強以前標準庫的bind1st和bind2st,讓咱們的用起來更方便!