cocos2d-x 回撥函式詳解
阿新 • • 發佈:2019-02-04
[cpp]
view plaincopyprint?
本質上,就是函式指標的應用。
下面是我寫的一個demo,類似cocos2d-x的實現:
[cpp] view plaincopyprint?
實現:
[cpp] view plaincopyprint?
main
[cpp] view plaincopyprint?
輸出:
[cpp] view plaincopyprint?
如果再定義一個巨集:
[cpp] view plaincopyprint?
那麼呼叫就改成:
[cpp] view plaincopyprint?
- typedefvoid (CCObject::*SEL_CallFuncN)(CCNode*);// 帶執行者回調
- typedefvoid (CCObject::*SEL_CallFuncND)(CCNode*, void*); // 帶一個自定引數的回撥
- typedefvoid (CCObject::*SEL_CallFuncO)(CCObject*);
- typedefvoid (CCObject::*SEL_MenuHandler)(CCObject*);
- typedefvoid (CCObject::*SEL_EventHandler)(CCEvent*);
- typedefint (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)
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?- typedefvoid (*func)(int x);
- void up(int s);
- func f= up;
- 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。
這樣呼叫:
- (m_pListener->*m_pSelector)(CCObject *param);
(m_pListener->*m_pSelector)(CCObject *param);
下面是我寫的一個demo,類似cocos2d-x的實現:
[cpp] view plaincopyprint?
- #ifndef __TestCallBack__Person__
- #define __TestCallBack__Person__
- #include <iostream>
- #include <string>
- usingnamespace std;
- // 基類
- class Person {
- public:
- void name(string name);
- };
- // 定義基類的成員函式指標
- typedefvoid (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;
- };
#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?
- #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;
- }
#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?
- #include <iostream>
- #include "Person.h"
- int main(int argc, constchar * argv[])
- {
- Student *a = new Student("Join",20);
- a->say();
- 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?
- Hi this is a Student
- My name is Join
- age is 20
Hi this is a Student
My name is Join
age is 20
如果再定義一個巨集:
[cpp] view plaincopyprint?
- #define callFunc_selector(_SELECTOR) (SEL_CallFun)(&_SELECTOR)
#define callFunc_selector(_SELECTOR) (SEL_CallFun)(&_SELECTOR)
那麼呼叫就改成:
[cpp] view plaincopyprint?
- m_pfnSelectior = callFunc_selector(Student::callBack);
m_pfnSelectior = callFunc_selector(Student::callBack);
這個就是cocos2d-x的回撥實現模式了。呵呵
仔細看看,是不是一樣。