定时器在Windows 的程序中的作用不可忽略,也随处可见。设定一个时间间隔每0.5秒或者1秒钟刷新一次时钟,这样就可以完成一个简单的电子钟程序。在不同的编程工具中定时器的用法也不同,Visual C++中也给我们提供了实现这种功能的方法,而且方法不只一种。在窗口类中是使用定时器比较很简单,用SetTimer()设置了定时器之后,并在Class Wizard中添加了WM_TIMER消息映射后,您就可以在映射函数OnTimer()中添加代码实现,来定时完成您的任务,而且还支持任意多个定时器,这种方法大家可能都会用。但是在非窗口的类中,使用定时器就没那么简单了,在类消息映射中就找不到OnTimer()方法了,类中也没有hWnd这个属性,SetTimer()也不能象原来那样使用了,下面给出了一种既不破坏类的完整性的同时又能巧妙的使用定时器的方法。程序运行后的界面效果如图一所示:
图一、定时器界面效果图 |
一、实现方法
在非窗口类中使用定时器,需要了解的知识比较多。首先非窗口类中没有消息映射,也没有象CWnd类具有的SetTimer()方法来设置定时器。没有消息映射,就只能靠我们自己定义的回调函数来处理定时器的消息,因此大家有必要了解一下回调函数的概念。因为回调函数只能用全局函数或者静态成员函数来实现,而为了维持类的完整性,又要求使用类的静态成员函数来作为回调函数,所以我们又需要了解一下静态数据成员和静态成员函数的性质。又因为定时器是在我们的程序中产生的,这又需要来管理定时器,所以又用到了映射表类CMap,因此介绍一下CMap的简单用法也是必不可少的。
所谓回调函数就是按照一定的形式由开发人员定义并编写实现内容,当发生某种事件时由系统或其它函数来调用的函数。使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己编写的一个函数(也就是回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,也就是某种事情发生的时候,利用传递的函数地址调用回调函数,这时开发人员可以利用这个机会在回调函数中处理消息或完成一定的操作。回调函数只能是全局函数,或者是静态函数,因为这个函数只是在类中使用,所以为了维护类的完整性,我们用类的静态成员函数来做回调函数。
在C语言中,声明一个数据为静态类型,意味着该变量的生存周期是静态的,即在程序的开始时即分配,到程序终止时才释放。但在C++中,声明一个类中的成员为静态类型,则意味着该类的所有实例只有该成员的一个拷贝。也就是说,不管应用程序中创建了这个类的多少个对象,其静态成员只有一个副本,该副本为这个类的所有对象实例所共享,而对于非静态成员,每个类对象实例都有自己的拷贝。例如一个公司职员类的定义如下:
class CPerson { public: CString szName; static CString szCompanyName; CPerson(); virtual ~CPerson(); }; |
对于同一家公司员工,每个人都有不同的姓名,但是他们的公司名字是一样的,所以就可以用一个静态类型来保存,这样所有的员工都共享这个公司名称,只要一位员工更新了公司名称,则所有员工的公司名称就被更新了。
静态成员被当作该类类型的全局对象,可以把一个静态数据成员和静态成员函数当成全局变量和函数那样去存储和访问,但又被隐藏在类的内部,并且清楚地与这个类相联系但又不是全局对象,同全局对象相比,使用静态成员有两个优势:
(1) 静态成员没有进入程序的全局名字空间,它属于类,它的名字只在类的范围内有效,因此不存在与程序中其他全局名字冲突的可能性。
(2) 可以实现信息隐藏,并可以保持类的完整性,可以是private(私有的)成员、public(公有的)成员或者protected(保护的)成员,而全局对象不能。
使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,就可以保证所有对象都能够访问到被更新后的值,这样可以提高效率和节省内存空间。
在类中将一个成员变量声明为静态的,与声明普通变量的唯一区别就是在其定义前加一个static,象上面的例子中那样声明:static CString szCompanyName;静态数据成员显式初始化与一般数据成员初始化不同。静态数据成员显式初始化的格式如下:
<数据类型><类名>::<静态数据成员名>=<值>
对于上面的例子这样初始化:CString CPerson::szCommpanyName = "天极网";
这表明:
(1) 初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆。
(2) 初始化时不加该成员的访问权限控制符private,public等。
(3) 初始化时使用作用域运算符来标明它所属类,因此,静态数据成员是类的成员,而不是对象的成员。