#
超级怪物在北京时间2009年6月26日——也就是今天逝世了,曾经是多么的喜爱这位歌星,被他的舞步感动得不可自控,模仿着他的步伐,欣赏着他的歌喉,感受着他带给音乐世界的疯狂。
不禁要感叹,人死的真是太快了......
boost::bind(&memberfunction, obj, _1, _2........)类似这样的用法,我们叫做成员函数绑定,boost库的文档中说的很清楚,第一个参数可以是value、pointer和reference,即传值、传地址和传引用都是可以的,所以在一般情况下,下面三种使用bind的形式都是成立的。
class A
{
public:
void func();
};
A a;
A& r = a;
boost::bind(&A::func, a);
boost::bind(&a::func, &a);
boost::bind(&a::func, r);
由上面的代码可以看出,我们可以随便传任意一种类对象的形式,函数模板会自动寻找最为匹配的为我们实现。但是有两种情况是特殊的,即:
1、该对象不可进行拷贝构造函数。
2、该对象不可随意被析构。
发现这个问题是在我编写单件模式时的遇见的,当时发现我的单件对象在bind中被析构了一次,这很不寻常,为什么bind会调用第一个参数的析构呢?跟踪进了boost的源码才发现,原来所有的参数都会被拷贝一遍,然后析构一遍,这样一来,我们传递参数的时候就会有一些小麻烦了,首先必须保证参数能够被拷贝而不影响逻辑和数据一致性,其次,参数能够被析构而不影响逻辑和数据一致性。单件是全局性质的数据,所以绝对不可以析构,那么这种情况的话,我们只好传递单件对象的地址,而不能传递值或引用。
另:附上出错问题的代码如下
class InputDevice
: public EventSource
, public Singleton<InputDevice>
{
public:
};
class TestUI
: public Singleton<TestUI>
{
public:
~TestUI(){
std::cout<<"~TestUI"<<std::endl;
}
void processKeyboard(EventArgs& args){
std::cout<<"键盘响应"<<std::endl;
}
void processMouse(EventArgs& args){
std::cout<<"鼠标响应"<<std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
new FrameUpdaterManager;
new DelayEventSender;
new InputDevice;
new TestUI;
InputDevice::getSingleton().mEventSet.addEvent("KeyDown", Event());
InputDevice::getSingleton().mEventSet.addEvent("KeyUp", Event());
InputDevice::getSingleton().mEventSet.addEvent("MouseLDown", Event());
InputDevice::getSingleton().mEventSet.addEvent("MouseLUp", Event());
InputDevice::getSingleton().mEventSet.addEvent("MouseRDown", Event());
InputDevice::getSingleton().mEventSet.addEvent("MouseRUp", Event());
//TestUI& ui = TestUI::getSingleton(); // 用此行便会出错
TestUI* ui = TestUI::getSingletonPtr();
// 出错开始
InputDevice::getSingleton().mEventSet["KeyDown"] += boost::bind(&TestUI::processKeyboard, ui, _1);
InputDevice::getSingleton().mEventSet["KeyUp"] += boost::bind(&TestUI::processKeyboard, ui, _1);
InputDevice::getSingleton().mEventSet["MouseLDown"] += boost::bind(&TestUI::processMouse, ui, _1);
InputDevice::getSingleton().mEventSet["MouseLUp"] += boost::bind(&TestUI::processMouse, ui, _1);
InputDevice::getSingleton().mEventSet["MouseRDown"] += boost::bind(&TestUI::processMouse, ui, _1);
InputDevice::getSingleton().mEventSet["MouseRUp"] += boost::bind(&TestUI::processMouse, ui, _1);
delete TestUI::getSingletonPtr();
delete InputDevice::getSingletonPtr();
delete DelayEventSender::getSingletonPtr();
delete FrameUpdaterManager::getSingletonPtr();
return 0;
}
log4cxx是一个不错的库,利用配置文件就可以很灵活的使用它。
说说具体用法,首先当然是在你的C++代码中写上Log的各种东西了
#include <log4cxx/logger.h>
#include <log4cxx/logstring.h>
#include <log4cxx/propertyconfigurator.h>
int _tmain(int argc, _TCHAR* argv[])
{
using namespace log4cxx;
// 读取配置文件
PropertyConfigurator::configure("log4cxx.cfg");
// 建立两个logger
LoggerPtr logger1 = Logger::getLogger("TraceYourMama");
LoggerPtr logger2 = Logger::getLogger("Patch");
LOG4CXX_TRACE(logger1, "跟踪");
LOG4CXX_WARN(logger1, "警告");
LOG4CXX_DEBUG(logger1, "调试");
LOG4CXX_ASSERT(logger1, false, "断言");
LOG4CXX_FATAL(logger1, "致命");
LOG4CXX_TRACE(logger2, "跟踪");
LOG4CXX_ERROR(logger2, "错误");
return 0;
}
其实在实际应用中,每个类都可以搞一个logger,然后在配置文件中进行如下的设置:
# 设置root logger为DEBUG级别
#log4j.rootLogger=TRACE,ca
log4j.logger.TraceYourMama=ERROR,fa,ha
log4j.logger.Patch=Trace,ca
#设置spirit为TRACE级别
#log4j.spirit=DEBUG
#log4j.additivity.spirit=false
# %m - message
# %n - 回车
# %d - 时间
# %.16c - Logger名称
# %-5p - log级别
# %t - thread_id
#对Appender ca进行设置:
#这是一个控制台类型的Appender
#输出格式(layout)为PatternLayout
log4j.appender.ca=org.apache.log4j.ConsoleAppender
log4j.appender.ca.layout=org.apache.log4j.PatternLayout
log4j.appender.ca.layout.ConversionPattern=%d %-5p %.16c - %m%n
#对Appender fa进行设置:
# 这是一个文件类型的Appender,
# 其输出文件(File)为./debug.log,
# 输出方式(Append)为覆盖方式,
# 输出格式(layout)为PatternLayout
log4j.appender.fa=org.apache.log4j.FileAppender
log4j.appender.fa.File=./debug.log
log4j.appender.fa.Append=true
log4j.appender.fa.layout=org.apache.log4j.PatternLayout
#log4j.appender.fa.layout.ConversionPattern=%d [%t] %-5p %.16c %x - %m%n
log4j.appender.fa.layout.ConversionPattern=%d %-5p %.16c - %m%n
接触了boost的状态机,发现不是想象中的那么好用,在一些地方还得用上mpl库里的东西,由于对模板元编程不是很熟练,搞了好些天才算弄明白这该死的mpl::list的原理和用法。
boost的状态机是属于静态链接的状态机,也就是说,它的图结构是编译期间就确定了的,在运行时不可以动态配置。所以,它的用途是有一定局限性的,但在一般情况下,它不仅很通用,而且在你会用并熟练地情况下,还会很好用,用起来很舒服,逻辑也很合理。下面就是一段代码,当然也是借鉴了别人的东西,自己修改了一下,在MainState中添加了一个Transition做了测试,因为此前我还不知道一个状态如何包含多个Transition,呵呵,原来是用mpl::list来做。至于这个状态机的入门教程,网上随处可见的三部曲:《boost 状态机入门教程》说得很清楚。
1 #include <iostream>
2 #include <ctime>
3
4 #include <boost/statechart/transition.hpp>
5 #include <boost/statechart/event.hpp>
6 #include <boost/statechart/state_machine.hpp>
7 #include <boost/statechart/simple_state.hpp>
8
9 namespace sc = boost::statechart;
10
11
12
13 class EvtStartStop : public sc::event<EvtStartStop>{};
14 class EvtReset : public sc::event<EvtReset>{};
15 class EvtGo : public sc::event<EvtGo>{};
16
17
18 class MainState;
19 class StopState;
20 class RunState;
21 class TwoState;
22
23 class Machine : public sc::state_machine<Machine, MainState>
24 {};
25
26
27
28
29
30
31 class MainState : public sc::simple_state<MainState, Machine, StopState>
32 {
33 public:
34 typedef sc::transition<EvtReset, MainState> reactReset;
35 typedef sc::transition<EvtGo, TwoState> reactGo;
36 typedef boost::mpl::list<reactReset, reactGo> reactions;
37
38 MainState(void){
39 std::cout<<"进入MainState"<<std::endl;
40 mTime = 0;
41 }
42
43 ~MainState(void){
44 std::cout<<"退出MainState"<<std::endl;
45 }
46
47 double mTime;
48 };
49
50
51 // 该状态属于无用状态,用于测试mpl::list的多transition用法
52 class TwoState : public sc::simple_state<TwoState, Machine>
53 {
54 public:
55 typedef sc::transition<EvtGo, MainState> reactions;
56
57 TwoState(void){
58 std::cout<<"进入TwoState"<<std::endl;
59 }
60
61 ~TwoState(void){
62 std::cout<<"退出TwoState"<<std::endl;
63 }
64 };
65
66
67 class StopState : public sc::simple_state<StopState, MainState>
68 {
69 public:
70 typedef sc::transition<EvtStartStop, RunState> reactions;
71 StopState(void){
72 std::cout<<"进入StopState"<<std::endl;
73 }
74
75 ~StopState(void){
76 std::cout<<"退出StopState"<<std::endl;
77 }
78 };
79
80 class RunState : public sc::simple_state<RunState, MainState>
81 {
82 public:
83 typedef sc::transition<EvtStartStop, StopState> reactions;
84 RunState(void){
85 std::cout<<"进入RunState"<<std::endl;
86 mStartTime = 0;
87 }
88
89 ~RunState(void){
90 std::cout<<"退出RunState"<<std::endl;
91 context<MainState>().mTime += std::difftime(std::time(0), mStartTime);
92 }
93
94 std::time_t mStartTime;
95 };
96
97
98 int _tmain(int argc, _TCHAR* argv[])
99 {
100 Machine mc;
101 mc.initiate();
102
103 mc.process_event(EvtStartStop());
104 mc.process_event(EvtStartStop());
105 mc.process_event(EvtReset());
106 mc.process_event(EvtGo());
107 mc.process_event(EvtGo());
108
109 return 0;
110 }
写模版多了的人,必然会遇到这样那样的问题,模版特例化就是解决了一个经常碰见的问题:如果我们需要根据某些特殊的模版参数类型来重新实现一下算法的话,该怎么办呢?说的多了那也没什么用,我们就写一个会做除法的类吧。这个类在做整数除法的时候会同时求出余数,而在算其他类型时是不求的。代码如下:
1#include <iostream>
2
3
4template<typename Ty>
5class Division
6{
7public:
8 Division(Ty dividend, Ty divisor)
9 :mDividend(dividend), mDivisor(divisor){}
10public:
11 void show()
12 {
13 std::cout<<"结果 = "<<mDividend/mDivisor<<std::endl;
14 }
15
16 Ty mDividend, mDivisor;
17};
18
19template<>
20inline void Division<int>::show()
21{
22 std::cout<<"结果 = "<<mDividend/mDivisor<<std::endl;
23 std::cout<<"余数 = "<<mDividend%mDivisor<<std::endl;
24}
25
26
27
28
29int _tmain(int argc, _TCHAR* argv[])
30{
31 Division<int> a(20, 11);
32 a.show();
33
34 Division<float> b(20.0f, 11.0f);
35 b.show();
36 return 0;
37}
以上这种方式就叫做局部特化,关于全部特化和局部特化、全特化和偏特化,我认为他们之间的关系很紧密,界限也很模糊,硬要分类的话,以上面的Division类为例,就看下面的代码对全部特化和局部特化的区别吧:
1 /**
2 @remark
3 Division的全部特化
4 */
5 template<>
6 class Division<int>
7 {
8 public:
9 Division(Ty dividend, Ty divisor)
10 :mDividend(dividend), mDivisor(divisor){}
11 public:
12 void show()
13 {
14 std::cout<<"结果 = "<<mDividend/mDivisor<<std::endl;
15 std::cout<<"余数 = "<<mDividend%mDivisor<<std::endl;
16 }
17
18 Ty mDividend, mDivisor;
19 };
20
21 /**
22 @remark
23 Division的局部特化
24 */
25 template<>
26 inline void Division<int>::show()
27 {
28 std::cout<<"结果 = "<<mDividend/mDivisor<<std::endl;
29 std::cout<<"余数 = "<<mDividend%mDivisor<<std::endl;
30 }
上面的代码只是说明了全部特化和局部特化,并没有说明什么事全特化和偏特化。其实,对于只有一个参数的模版类型来说,并没有什么全特化和偏特化的区别,但如果是一个以上的模板参数,那就是由区别的了,如下:
1 /**
2 @remark
3 原始模板类
4 */
5 template<typename Ty, typename TyEx>
6 class ObjectData
7 {
8 //
9 };
10
11 /**
12 @remark
13 Object的全特化
14 */
15 template<>
16 class ObjectData<int, float>
17 {
18 //
19 };
20
21 /**
22 @remark
23 Object的偏特化,只特化第一个参数
24 */
25 template<typename TyEx>
26 class ObjectData<int, TyEx>
27 {
28 //
29 };
以上仅为个人观点,错误之处请指正。