cocos2d-x回調函數詳解

   回調是cocos2d-x中一個很重要的部分,我目前參與的一個項目中就大量的用到了回調,今天“一不小心”就在網上看到了一篇關於cocos2d-x回調的優質博文,其中從函數指針的角度,詳細的講述了回調的實現機制,最重要的是其中提出了一個“成員函數指針”的概念,很多朋友可能都不知道,函數指針與成員函數指針的區別,不羅嗦,上博文!


博客地址: http://blog.csdn.net/fylz1125/article/details/8546607


cocos2d-x中有大量的回調函數的應用,主要有以下幾類,看下CCObject.h中的定義

typedef void (CCObject::*SEL_SCHEDULE)(float);// 用來調update 
typedef void (CCObject::*SEL_CallFunc)();// 用來自定義無參回調 
typedef void (CCObject::*SEL_CallFuncN)(CCNode*);// 帶執行者回調 
typedef void (CCObject::*SEL_CallFuncND)(CCNode*, void*); // 帶一個自定參數的回調 
typedef void (CCObject::*SEL_CallFuncO)(CCObject*); 
typedef void (CCObject::*SEL_MenuHandler)(CCObject*); 
typedef void (CCObject::*SEL_EventHandler)(CCEvent*); 
typedef int (CCObject::*SEL_Compare)(CCObject*); 
                                                               
#define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR) 
#define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR) 
#define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR) 
#define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR) 
#define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR) 
#define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR) 
#define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR) 
#define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)

本質上,就是函數指針的應用。


但是,我們知道,在C中,函數指針是很普遍的應用。一般函數的函數名就是指針,不過是常量,再定義一個函數指針就是一個變量,這個變量可以指向這一類函數的地址。

比如:

typedef void (*func)(int x); 
void up(int s); 
func f= up; 
f(3);

func是個函數指針類型:返回值是void,參數是一個int的函數。所以func的變量可以指向所有這一類的函數。
這是C風格的函數指針。但是在cocos2d-x中的回調,雖然還是函數指針,但已經有所區別。準確點說應該是成員函數指針。那麼這普通的函數指針還可以來調成員函數嗎?呵呵,如果能的話我就不用寫這篇文章了。
C風格的函數指針要想調用成員函數,那麼這個成員函數如果是static的也可以(爲什麼靜態函數就可以,呵呵)。但是這樣的話就會破壞類的結構。看cocos2d-x的實現也不是這樣的。
這裏說cocos2d-x的實現方式:
看上面的定義,如:typedef void (CCObject::*SEL_MenuHandler)(CCObject*);
看這個就應該大致可以知道它的實現了。
這個定義有點不一樣,就是這個函數是CCObject的成員函數。這就是成員函數指針的定義。
大家知道,成員函數不能像普通C風格的函數那樣調用,因爲每個成員函數需要知道是哪個對象實例調用它的,隱含有一個this指針。這也解釋了爲什麼靜態函數可以用C風格的函數指針來回調,因爲靜態函數不需要對象實例就可以調用,呵呵。
既然定義成員函數指針,那麼要用這個指針變量來調用回調函數,還需不需要對象實例呢。毫無疑問,還是需要的。
所以還必須有一個回調對象,CCObject *m_pListener。
這樣調用:

(m_pListener->*m_pSelector)(CCObject *param);

下面是我寫的一個demo,類似cocos2d-x的實現:

#ifndef __TestCallBack__Person__ 
#define __TestCallBack__Person__ 
                                                 
#include <iostream> 
#include <string> 
                                                 
using namespace std; 
                                                 
// 基類 
class Person
{                                        
public: 
    void name(string name); 
}; 
                                                 
// 定義基類的成員函數指針 
typedef void (Person::*SEL_CallFun)(string str); 
                                                 
                                                 
// 派生類 
class Student : public Person
{ 
private: 
    string m_name; 
    int m_age; 
                                                 
public: 
    Student(string name, int age); 
    ~Student(); 
                                                 
    // 回調 
    void callBack(string str); 
                                                 
    // say方法,要調用回調函數。 
    void say(); 
protected: 
    // 回調的執行者 
    Person *m_pListen; 
                                                 
    // 回調函數指針 
    SEL_CallFun m_pfnSelectior; 
};

實現:

#include "Person.h" 
                                            
void Person::name(string name) 
{ 
    cout<<name<<endl; 
} 
                                            
Student::Student(string name, int age) 
{ 
    this->m_name = name; 
    this->m_age = age; 
} 
                                            
Student::~Student() 
{                                      
} 
                                            
void Student::say() 
{ 
    cout<<"Hi this is a Student"<<endl; 
                                            
    // 回調函數指針賦值。需要強轉成 SEL_CallFun 
    m_pfnSelectior = (SEL_CallFun)(&Student::callBack); 
                                            
    // 回調的執行對象,傳this 
    m_pListen = this; 
                                            
    // 調用回調,參數是個string 
    (m_pListen->*m_pfnSelectior)(m_name); 
} 
                                            
// 成員函數,要回調的函數 
void Student::callBack(string str) 
{ 
    cout<<"My name is " 
    << str<<endl 
    << "age is " 
    <<m_age<<endl; 
}

main:

#include <iostream> 
#include "Person.h" 
                                    
int main(int argc, const char * argv[]) 
{                                 
    Student *a = new Student("Join",20); 
    a->say(); 
    return 0; 
}

輸出:

Hi this is a Student 
My name is Join 
age is 20

如果再定義一個宏:

#define callFunc_selector(_SELECTOR) (SEL_CallFun)(&_SELECTOR)

那麼調用就改成:

m_pfnSelectior = callFunc_selector(Student::callBack);

這個就是cocos2d-x的回調實現模式了。呵呵
仔細看看,是不是一樣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章