自从上次写完借(chao)鉴(xi)cocos2dx的消息管理机制之后,也在实际中试着用了几次,只能说对于继承Ref基类方面,一直是最大的不足之处,而后更是遇到了一个问题,使我不得不抛弃这个方式,转而寻找别的方法。
问题(bug):在多重继承中,对于子类如果是先继承别的类,在订阅消息强转成Ref类型指针时,由于c++对象的结构,强转过程中会发生地址偏移,同时在调用时对象每个成员偏移,最后导致在接受消息的函数中对成员变量操作时无效。起初我以为是对象强转用的是static_cast的原因,试着换成dynamic_cast,然并卵。后经同事提醒知道是这个原因,随后去用cocos2dx的__NotificationCenter测试,发现也是存在这个问题,没有解决,哈哈,也不知道这算不算引擎bug。
解决方案:c++中有个function和bind模板,将函数和对象打包成一个函数,可以在类外调用,类似于c#中的delegate,但更强大,通过这个方法完美解决,同时顺道解决了继承问题,不再需要继承Ref的形式了。
1.NotificationObserver观察者对象类
与上一篇相同,存储订阅消息的对象,以及消息名字,函数指针等一系列的数据信息。NotificationObserver类写在NotificationCenter.h中,并且所有函数体也都直接实现了。
//观察者类,负责管理通知订阅消息的类
class NotificationObserver
{
public:
NotificationObserver(std::function<void(int)> _func, string _Messagename)
{
m_Messagename = _Messagename;
m_func = _func;
}
~NotificationObserver()
{
}
string GetName()
{
return m_Messagename;
}
void ObserverCallBack(int ref)
{
if (m_func)
{
m_func(ref);
}
}
function<void(int)> GetFunction()
{
return m_func;
}
//重载运算符
bool operator ==(NotificationObserver* _observe)const
{
return m_Messagename == _observe->m_Messagename;
}
private:
string m_Messagename;//消息名字 唯一标签,用以发送消息时消息管理器根据该标签来判定调用哪个回调函数
std::function<void(int)> m_func;//c++ function模板,存储回调函数
};
这里单拿出来其实只是凑篇幅,分条理。
2.NotificationCenter消息管理中心
消息中心必须是单例,在一个程序中只能有一个存在,只是将原本的方式改成了function模板。
NotificationCenter.h:
#ifndef __NOTIFICATION_CENTER_
#define __NOTIFICATION_CENTER_
#pragma once
//#include"Ref.h"
#include <iostream>
#include<vector>
#include <list>
#include<functional>//这个是function和bind模板的库
using namespace std;
using namespace std::placeholders;//这个是bind模板中动态参数的命名空间
//默认函数带一个int型的参数
class NotificationObserver;
class NotificationCenter
{
public:
NotificationCenter();
~NotificationCenter();
static NotificationCenter*getInstance();
static void destroyInstance();
//添加观察者
//订阅消息的函数,名字标签
void addObserver(std::function<void(int)> _func, string _Messagename);
//移除观察者
void removeObserver(string _Messagename);
//清空观察者
void removeAllObservers();
//判断该观察者是否已经添加过了
bool ObserverExisted(function<void(int)> _func, string _Messagename);
//发送消息
void PostNotification(string _Messagename);
//带参数的发送消息
void PostNotification(string _Messagename, int _ref);
private:
vector<NotificationObserver*> m_array;
};
//观察者类,负责管理通知订阅消息的类
class NotificationObserver
{
public:
NotificationObserver(std::function<void(int)> _func, string _Messagename)
{
m_Messagename = _Messagename;
m_func = _func;
}
~NotificationObserver()
{
}
string GetName()
{
return m_Messagename;
}
void ObserverCallBack(int ref)
{
if (m_func)
{
m_func(ref);
}
}
function<void(int)> GetFunction()
{
return m_func;
}
//重载函数运算符
bool operator ==(NotificationObserver* _observe)const
{
return m_Messagename == _observe->m_Messagename;
}
private:
string m_Messagename;
std::function<void(int)> m_func;
};
#endif
NotificationCenter.cpp :
#include "NotificationCenter.h"
static NotificationCenter *_notification = nullptr;
NotificationCenter::NotificationCenter()
{
m_array.clear();
}
NotificationCenter::~NotificationCenter()
{
}
NotificationCenter*NotificationCenter::getInstance()
{
if (_notification != nullptr)
{
return _notification;
}
_notification = new NotificationCenter;
return _notification;
}
void NotificationCenter::destroyInstance()
{ if(_notification!=nullptr)
delete _notification;
}
bool NotificationCenter::ObserverExisted(std::function<void(int)> _func, string _Messagename)
{
NotificationObserver *obj = nullptr;
NotificationObserver* _observer=new NotificationObserver(_func,_Messagename);
bool _existed = false;
for each (obj in m_array)
{
if (!obj)
{
continue;
}
if (obj==_observer)
{
_existed=true;
break;
}
}
delete _observer;
return _existed;
}
void NotificationCenter::addObserver(std::function<void(int)> _func, string _Messagename)
{
if (this->ObserverExisted(_func, _Messagename))
{
return;
}
NotificationObserver *observe = new NotificationObserver(_func, _Messagename);
m_array.push_back(observe);
}
void NotificationCenter::removeObserver(string _Messagename)
{
//vector<NotificationObserver*>::iterator itor;
auto itor = m_array.begin();
int i = 0;
for (itor = m_array.begin(); itor != m_array.end();)
{
if (((*itor)->GetName() == _Messagename))
{
//delete m_array.at(i);
delete *itor;
itor = m_array.erase(itor);
}
else{
i++;
itor++;
}
}
}
void NotificationCenter::removeAllObservers()
{
vector<NotificationObserver*>::iterator itor;
itor = m_array.begin();
for (itor = m_array.begin(); itor != m_array.end();)
{
delete *itor;
itor++;
}
m_array.clear();
}
//发送消息
void NotificationCenter::PostNotification(string _Messagename)
{
int _ref = 0;
PostNotification(_Messagename, _ref);
}
void NotificationCenter::PostNotification(string _Messagename, int _ref)
{
for (auto sp : m_array)
{
if (sp->GetName() == _Messagename)
{
sp->ObserverCallBack(_ref);
}
}
}
附注:默认是带有一个int类型的参数,但是在postNotification时,可以只发送消息不带参数,如果你的函数不需要这个参数的话,所以到这里已经完善了,坐等测试。
3.测试样例
测试就更简单了,类似于cocos2dx 的__NotificationCenter使用方式,先定义一个类,不用继承任何类,当然也可以随便继承
Sprite.h:
Sprite.cpp :
// C++Test.cpp : 定义控制台应用程序的入口点。
#include "stdafx.h"
#include"Sprite.h"
#include"NotificationCenter.h"
using namespace std;
using namespace stdext;
void MainTest()
{
auto sprite=new Sprite();
NotificationCenter::getInstance()->addObserver(bind(&Sprite::test,sprite,_1), "test");//bind 的第一个参数是sprite的test成员函数的引用,sprite是对象,_1是std::placeholders的类型,_1表示第一个参数,以此类推,_2是第二个参数,所以可以不光传一个int类型的,可以传多个参数,只是需要自己改NotificationCenter类
NotificationCenter::getInstance()->addObserver(bind(&Sprite::dosomthing,sprite,_1), "dosomthing");
NotificationCenter::getInstance()->PostNotification("test");
NotificationCenter::getInstance()->PostNotification("dosomthing", 123);
delete sprite;
}
int _tmain(int argc, _TCHAR* argv[])
{
MainTest();
return 0;
}
运行视图: