分类: C/C++
2008-03-14 14:22:29
摘 要:This page introduce how to use timer in window class and none window class of Visual C++ by some simple samples. Use timer in none window class with static member variable and static member function is the important point. At the same time, it also tell about of some knowledge such as about timer, callback function, static member of C++ class and map class CMap of template class.
关键字:C++ Class Timer static CALLBACK CMap
一、引言
定时器在Windows 的程序中的作用不可忽略,也随处可见。设定一个时间间隔每0.5秒或者1秒钟刷新一次时钟,这样就可以完成一个简单的电子钟程序。在不同的编程工具中定时器的用法也不同,Visual C++中也给我们提供了实现这种功能的方法,而且方法不只一种。在窗口类中是使用定时器比较很简单,用SetTimer()设置了定时器之后,并在Class Wizard中添加了OnTimer消息映射后,您就可以在映射函数OnTimer()中添加代码实现,来定时完成您的任务,而且还支持任意多个定时器,这种方法大家可能都会用。但是在非窗口的类中,使用定时器就没那么简单了,在类消息映射中就找不到OnTimer()方法了,类中也没有hWnd这个属性,SetTimer()也不能象原来那样使用了,下面给出了一种既不破坏类的完整性的同时又能巧妙的使用定时器的方法。
二、相关知识
在非窗口类中使用定时器,需要了解的知识比较多。首先非窗口类中没有消息映射,也没有象CWnd类具有的SetTimer()方法来设置定时器。没有消息映射,就只能靠我们自己定义的回调函数来处理定时器的消息,因此大家有必要了解一下回调函数的概念。因为回调函数只能用全局函数或者静态成员函数来实现,为了维持类的完整性,使用类的静态成员函数来作为回调函数,所以我们又需要了解一下静态数据成员和静态成员函数的性质。又因为定时器是在我们的程序中产生的,这又需要来管理定时器,所以又用到了映射表类CMap,因此介绍一下CMap的简单用法也是必不可少的。
2.1 回调函数
所谓回调函数就是按照一定的形式由你定义并编写实现内容,当发生某种事件时,而由系统或其它函数来调用的函数。
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己编写的一个函数(也就是回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,也就是某种事情发生的时候,利用传递的函数地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。回调函数只能是全局函数,或者是静态函数,因为这个函数只是在这个类中使用,所以为了维护类的完整性,我们用类的静态成员函数来做回调函数。
2.2 C++类中的静态成员
在C语言中,声明一个数据为静态类型,意味着该变量的生存周期是静态的,即在程序的开始时即分配,到程序终止时才释放。但在C++中,声明一个类中的成员为静态类型,则意味着该类的所有实例只有该成员的一个拷贝。也就是说,不管应用程序中创建了这个类的多少个对象,其静态成员只有一个副本,该副本为这个类的所有对象实例所共享,而对于非静态成员,每个类对象实例都有自己的拷贝。例如:
class CPerson { public: CString szName; static CString szCompanyName; CPerson(); virtual ~CPerson(); };接着用该类声明一个实例 CPerson me;
在类中将一个成员变量声明为静态的,与声明普通变量的唯一区别就是在其定义前加一个static。
象上面的例子中那样声明:
static CString szCompanyName;静态数据成员显式初始化与一般数据成员初始化不同。静态数据成员显式初始化的格式如下:
CString CPerson::szCommpanyName = "网进科技";这表明:
CString TheCommpanyName = me.CommpanyName;(2) 因为类静态数据成员只有一个拷贝,所以它不一定要通过对象或者指针来访问。方法二就是用被类名限定修饰的名字直接访问它。当我们不通过类的成员访问操作符访问静态数据成员时,必须指定类名以及紧跟其后的域操作符,因为静态成员不是全局对象,所以我们不能在全局域中找到它。如:
CString TheCommpanyName = CPerson::CommpanyName;顺便说一句静态数据成员还有两个特点:
class CPerson { public: //该实例的一句座右铭 CString szMotto; //用于保存该实例的指针 CPerson* pThis; //非静态成员函数,弹出该实例的座右铭 void GetMotto(); //静态成员函数,弹出该实例的座右铭 static void GetMottoStaic(CPerson* pPerson); CPerson(); virtual ~CPerson(); };Person.cpp文件如下:
#include "stdafx.h" #include "Person.h" CPerson::CPerson() { pThis = this; } CPerson::~CPerson() { } void CPerson::GetMotto() { AfxMessageBox(szMotto); } void CPerson::GetMottoStaic(CPerson* pPerson) { pPerson->GetMotto(); }在需要的地方就可以如下访问静态成员函数:
m_Person.szMotto = "我的座右铭是:这是由静态函数访问非静态函数的结果!"; m_Person.GetMottoStaic(m_Person.pThis);其实这个例子在实际上是没有什么意义的,这样做的目的只是为了演示如何实现这个方法而已。
UINT SetTimer( UINT nIDEvent, UINT nElapse, void (CALLBACK EXPORT* lpfnTimer)(HWND, UINT, UINT, DWORD));这个函数是CWnd类的一个成员函数,其参数意义如下:
SetTimer(1,1000,NULL);⑵ 通过Class Wizard给主窗口类添加一个WM_TIMER消息的映射函数,默认为OnTimer(UINT nIDEvent)。
switch(nIDEvent) { case 1: CTime m_SysTime = CTime::GetCurrentTime(); SetDlgItemText(IDC_STATIC_TIME,m_SysTime.Format("%Y年%m月%d日 %H:%M:%S")); break; }代码中的IDC_STATIC_TIME就是我们先前添加的Label控件的ID。
首先介绍一下用于设置定时的函数:
UINT SetTimer( HWND hWnd, // handle of window for timer messages UINT nIDEvent, // timer identifier UINT uElapse, // time-out value TIMERPROC lpTimerFunc // address of timer procedure );其中的参数意义如下:
VOID CALLBACK TimerProc( HWND hwnd, // handle of window for timer messages UINT uMsg, // WM_TIMER message UINT idEvent, // timer identifier DWORD dwTime // current system time );其中的参数意义如下:
m_nTimerID = SetTimer(NULL,NULL,nElapse,MyTimerProc);先通过Class Wizard创建一个非窗口类,选择Generic Class类类型,类名称为CMyTimer,该类的作用是每隔一段时间提醒我们做某件事情,然后用这个类创建三个实例,每个实例以不同的时间间隔提醒我们做不同的事情。
#includeMyTimer.cppclass CMyTimer; //用模板类中的映射表类定义一种数据类型 typedef CMap CTimerMap; class CMyTimer { public: //设置定时器,nElapse表示时间间隔,sz表示要提示的内容 void SetMyTimer(UINT nElapse,CString sz); //销毁该实例的定时器 void KillMyTimer(); //保存该实例的定时器标志值 UINT m_nTimerID; //静态数据成员要提示的内容 CString szContent; //声明静态数据成员,映射表类,用于保存所有的定时器信息 static CTimerMap m_sTimeMap; //静态成员函数,用于处理定时器的消息 static void CALLBACK MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime); CMyTimer(); virtual ~CMyTimer(); };
#include "stdafx.h" #include "MyTimer.h" //必须要在外部定义一下静态数据成员 CTimerMap CMyTimer::m_sTimeMap; CMyTimer::CMyTimer() { m_nTimerID = 0; } CMyTimer::~CMyTimer() { } void CALLBACK CMyTimer::MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime) { CString sz; sz.Format("%d号定时器:%s", idEvent, m_sTimeMap[idEvent]->szContent); AfxMessageBox(sz); } void CMyTimer::SetMyTimer(UINT nElapse,CString sz) { szContent = sz; m_nTimerID = SetTimer(NULL,NULL,nElapse,MyTimerProc); m_sTimeMap[m_nTimerID] = this; } void CMyTimer::KillMyTimer() { KillTimer(NULL,m_nTimerID); m_sTimeMap.RemoveKey(m_nTimerID); }这样就完成了在非窗口类中使用定时器的方法。以上这些代码都在Windwos 2000 Professional 和 Visual C++ 6.0中编译通过。
五、结论
通过以上的介绍,大家应该知道如何在静态成员函数中访问非静态数据成员和非静态成员函数,并了解了如何在非窗口类中使用定时器。当然这只是解决这个问题的一种方法,相信还有更好的解决办法。这个种方法有一定的灵活性,可以在很多地方用到,例如网络程序中的连接超时以及定时刷新等需要自己来控制,就可以使用这种方法。
参考文献:
1 潘爱民 张丽.C++ Primer 中文版.(第三版).北京:中国电力出版社.2002
2 Jeff Prosise.MFC Windows 程序设计.(第二版).北京:清华大学出版社.2001
3 王险峰,刘宝宏.Windows环境下的多线程编程原理与应用.北京:清华大学出版社.2002
4 侯俊杰.深入浅出MFC.台湾:松岗电脑图资料股份有限公司.1998
作者简介:
姓 名:刘辉
笔 名:我在人间
工作单位:网进科技昆山有限公司
联系方式:Email:jemmyliu@163.com
专业职称:软件工程师
研究方向:利用Visual C++进行软件开发
笔者注:这是我写的第一篇关于编程方面的技术文章,写这篇文章的目的只是把自己平时做项目和学习的过程中觉得比较用的方法,技巧与大家分享。其中难免有不少的错误,请来信指出!