1. 程式人生 > >cocos2d-x 回撥函式詳解

cocos2d-x 回撥函式詳解

[cpp] view plaincopyprint?
  1. typedefvoid (CCObject::*SEL_CallFuncN)(CCNode*);// 帶執行者回調
  2. typedefvoid (CCObject::*SEL_CallFuncND)(CCNode*, void*); // 帶一個自定引數的回撥
  3. typedefvoid (CCObject::*SEL_CallFuncO)(CCObject*); 
  4. typedefvoid (CCObject::*SEL_MenuHandler)(CCObject*); 
  5. typedefvoid (CCObject::*SEL_EventHandler)(CCEvent*); 
  6. typedefint (CCObject::*SEL_Compare)(CCObject*); 
  7. #define schedule_selector(_SELECTOR) (SEL_SCHEDULE)(&_SELECTOR)
  8. #define callfunc_selector(_SELECTOR) (SEL_CallFunc)(&_SELECTOR)
  9. #define callfuncN_selector(_SELECTOR) (SEL_CallFuncN)(&_SELECTOR)
  10. #define callfuncND_selector(_SELECTOR) (SEL_CallFuncND)(&_SELECTOR)
  11. #define callfuncO_selector(_SELECTOR) (SEL_CallFuncO)(&_SELECTOR)
  12. #define menu_selector(_SELECTOR) (SEL_MenuHandler)(&_SELECTOR)
  13. #define event_selector(_SELECTOR) (SEL_EventHandler)(&_SELECTOR)
  14. #define compare_selector(_SELECTOR) (SEL_Compare)(&_SELECTOR)
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中,函式指標是很普遍的應用。一般函式的函式名就是指標,不過是常量,再定義一個函式指標就是一個變數,這個變數可以指向這一類函式的地址。

比如:

[cpp] view plaincopyprint?
  1. typedefvoid (*func)(int x); 
  2. void up(int s); 
  3. func f= up; 
  4. f(3); 
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。
這樣呼叫:

[cpp] view plaincopyprint?
  1. (m_pListener->*m_pSelector)(CCObject *param); 
(m_pListener->*m_pSelector)(CCObject *param);



下面是我寫的一個demo,類似cocos2d-x的實現:
[cpp] view plaincopyprint?
  1. #ifndef __TestCallBack__Person__
  2. #define __TestCallBack__Person__
  3. #include <iostream>
  4. #include <string>
  5. usingnamespace std; 
  6. // 基類
  7. class Person { 
  8. public
  9. void name(string name); 
  10. }; 
  11. // 定義基類的成員函式指標
  12. typedefvoid (Person::*SEL_CallFun)(string str); 
  13. // 派生類
  14. class Student : public Person{ 
  15. private
  16. string m_name; 
  17. int m_age; 
  18. public
  19. Student(string name, int age); 
  20. ~Student(); 
  21. // 回撥
  22. void callBack(string str); 
  23. // say方法,要呼叫回撥函式。
  24. void say(); 
  25. protected
  26. // 回撥的執行者
  27. Person *m_pListen; 
  28. // 回撥函式指標
  29. SEL_CallFun m_pfnSelectior; 
  30. }; 
#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;
};



實現:
[cpp] view plaincopyprint?
  1. #include "Person.h"
  2. void Person::name(string name) 
  3. cout<<name<<endl; 
  4. Student::Student(string name, int age) 
  5. this->m_name = name; 
  6. this->m_age = age; 
  7. Student::~Student() 
  8. void Student::say() 
  9. cout<<"Hi this is a Student"<<endl; 
  10. // 回撥函式指標賦值。需要強轉成 SEL_CallFun
  11. m_pfnSelectior = (SEL_CallFun)(&Student::callBack); 
  12. // 回撥的執行物件,傳this
  13. m_pListen = this
  14. // 呼叫回撥,引數是個string
  15. (m_pListen->*m_pfnSelectior)(m_name); 
  16. // 成員函式,要回調的函式
  17. void Student::callBack(string str) 
  18. cout<<"My name is "
  19. << str<<endl 
  20. << "age is "
  21. <<m_age<<endl; 
#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
[cpp] view plaincopyprint?
  1. #include <iostream>
  2. #include "Person.h"
  3. int main(int argc, constchar * argv[]) 
  4. Student *a = new Student("Join",20); 
  5. a->say(); 
  6. return 0; 
#include <iostream>
#include "Person.h"

int main(int argc, const char * argv[])
{

Student *a = new Student("Join",20);
a->say();
return 0;
}


輸出:
[cpp] view plaincopyprint?
  1. Hi this is a Student 
  2. My name is Join 
  3. age is 20 
Hi this is a Student
My name is Join
age is 20



如果再定義一個巨集:
[cpp] view plaincopyprint?
  1. #define callFunc_selector(_SELECTOR) (SEL_CallFun)(&_SELECTOR)
#define callFunc_selector(_SELECTOR) (SEL_CallFun)(&_SELECTOR)


那麼呼叫就改成:
[cpp] view plaincopyprint?
  1. m_pfnSelectior = callFunc_selector(Student::callBack); 
m_pfnSelectior = callFunc_selector(Student::callBack);

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