c++自定义消息管理机制升级版(std::bind && std::function)

      自从上次写完借(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

#pragma once
#ifndef _SPRITE_H_
#define _SPRITE_H_
class Sprite
{
public:
	Sprite();
	~Sprite();
	 void test(int _ref);
	 void dosomthing(int _ref);
};#endif
 
 

Sprite.cpp
#include "Sprite.h"
#include <iostream>
#include"NotificationCenter.h"
Sprite::Sprite()
{
	/*this->dosomthing();
	NotificationCenter::getInstance()->addObserver(dynamic_cast<Ref*>(this), "test", callfunc_selector(Sprite::test));
	NotificationCenter::getInstance()->PostNotification("test");*/
}
Sprite::~Sprite()
{
}
void Sprite::test(int _ref)
{
	cout << "I am here :"<<_ref<<endl;
	return;
}
void Sprite::dosomthing(int _ref)
{
	printf("I am here to doing somthing :%d",_ref);
	return;
}
 
 
再看看Main函数:
// 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;
}

运行视图:
Over,其实一般写消息机制,回调函数,大概用function是比较常见的吧
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值