vc定时器怎么使用,
定时器在Windows程序中起着重要的作用,随处可见。设置一个时间间隔,每0.5秒或1秒刷新一次时钟,这样就可以完成一个简单的电子钟程序。定时器在不同的编程工具中的用法是不同的。Visual C也为我们提供了实现这个功能的方法,而且方法不止一个。在窗口类中使用timer非常简单。用SetTimer()设置定时器,在类向导中添加WM_TIMER消息映射后,可以在映射函数OnTimer()中添加代码实现,定时完成你的任务,也可以支持任意数量的定时器。每个人都可能使用这种方法。但是,在非窗口类中,使用计时器就没那么简单了。在类消息映射中找不到OnTimer()方法,并且该类中没有hWnd属性。SetTimer()不能像以前那样使用了。这里有一个方法,在不破坏类的完整性的情况下,巧妙地使用定时器。程序运行后的界面效果如图1所示:
一、实施方法
你需要知道更多关于在非窗口类中使用定时器的知识。首先,非window类中没有消息映射,也没有类似CWnd类的SetTimer()方法来设置定时器。没有消息映射,我们只能通过自己定义的回调函数来处理定时器消息,所以大家有必要知道回调函数的概念。因为回调函数只能由全局函数或者静态成员函数实现,而且为了维护类的完整性,要求使用类的静态成员函数作为回调函数,所以我们需要知道静态数据成员和静态成员函数的属性。因为定时器是在我们的程序中生成的,需要管理定时器,使用的是映射表类CMap,所以有必要介绍一下CMap的简单用法。
所谓回调函数,就是开发者以一定的形式定义和编写的,在某种事件发生时,由系统或其他函数调用的函数。其实在调用一个函数(通常是API函数)的时候,使用回调函数就是把自己写的一个函数(也就是回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,也就是有事的时候,用传递的函数地址调用回调函数。这时候开发者就可以利用这个机会在回调函数中处理消息或者完成某些操作。回调函数只能是全局函数或静态函数。因为这个函数只在类中使用,为了维护类的完整性,我们使用类的静态成员函数作为回调函数。
在C语言中,将一个数据声明为静态类型,意味着该变量的生命周期是静态的,即在程序开始时分配,只有在程序终止时才释放。但是,在C中,将类中的成员声明为静态类型意味着该类的所有实例都只有该成员的一个副本。也就是说,无论应用程序中创建了多少个该类的对象,其静态成员只有一个副本,由该类的所有对象实例共享。对于非静态成员,每个类对象实例都有自己的副本。例如,某公司员工类的定义如下:c类人员{ public:cstringszname;静态CString szCompanyNameCPerson();virtual ~ CPerson();};
对于同一个公司的员工,每个人的名字都不一样,但是他们的公司名是一样的,所以可以用静态类型保存,让所有员工共享这个公司名。只要一个员工更新公司名称,所有员工的公司名称都会更新。
静态成员被视为该类类型的全局对象,静态数据成员和静态成员函数可以作为全局变量和函数存储和访问,但它们隐藏在类内部,显然与该类相关,但不是全局对象。与全局对象相比,使用静态成员有两个优点:
(1)静态成员不进入程序的全局命名空间,它属于类,它的名字只在类的范围内有效,所以不存在与程序中其他全局名字冲突的可能。
(2)可以实现信息隐藏,保持类的完整性。它可以是私有(private)成员、公共(public)成员或受保护(protected)成员,但全局对象不能。
使用静态数据成员可以节省内存,因为它们对所有对象都是通用的。因此,对于多个对象,静态数据成员只存储在一个地方,供所有对象共享。静态数据成员的值对于每个对象都是相同的,但是它的值可以更新。只要静态数据成员的值更新一次,就可以保证所有对象都能访问更新后的值,这样可以提高效率,节省内存空间。
在类中声明一个成员变量为static,与声明一个公共变量的唯一区别是在它的定义前添加一个static,如上面的例子:static CString szCompanyName;静态数据成员的显式初始化不同于一般的数据成员初始化。静态数据成员的显式初始化格式如下:
数据类型类名:静态数据成员名=值
上面的例子初始化如下:cstring c person:szcommpanyname=天体网;
这表明:
(1)初始化在类外进行,前面不加static,以免与一般的静态变量或对象混淆。
(2)初始化时,成员的访问控制字符为私有、公共等。不添加。
(3)初始化时,作用域运算符用来表示所属的类。因此,静态数据成员是类的成员,而不是对象。您可以在类的成员函数中直接引用类的静态数据成员,而无需使用成员访问运算符。但是在非成员函数中,我们必须以两种方式之一访问静态数据成员。
(1)使用成员访问操作符。
比如me是CPerson的一个实例,其中的静态数据成员可以在非成员函数中应用如下:cstring the commpanyname=me . commpanyname;
(2)因为类静态数据成员只有一个副本,所以不必通过对象或指针访问。第二种方法是用类名限定和修饰的名称直接访问它。当我们不通过类的成员访问操作符访问静态数据成员时,我们必须指定类名和紧随其后的域操作符。因为静态成员不是全局对象,所以我们无法在全局域中找到它。例如cstring commpanyname=cperson:commpanyname;
对了,静态数据成员还有另外两个特点:第一,静态数据成员的类型可以是其所属的类,而非静态数据成员只能声明为该类对象的指针或引用;第二,静态数据成员可以作为类成员函数的默认实参,非静态成员不能。
静态成员函数声明和普通函数声明的唯一区别是它前面有一个静态。通常,当前对象的地址(this)被隐式传递给被调用的非静态成员函数。静态成员函数具有类的作用域。与非静态成员函数相比,静态成员函数没有这个参数,所以不能访问一般的数据成员,只能访问静态数据成员、枚举或嵌套类型等静态成员函数。这样使用静态成员函数的速度相对全局函数可以稍微提高一点。它不仅没有传递这个指针的额外开销,还具有在类中制作函数的优势。如果要引用静态成员函数中的非静态成员,可以通过对象来引用。我们可以使用成员访问操作符点(。)和箭头(-)来访问静态成员函数作为类对象或者类对象的指针,或者我们可以直接用限定修饰名来访问静态成员函数,而不用声明类对象。
静态成员函数受到以下约束:(1)不能用成员选择器访问非静态成员。或者-);(2)不能解释为虚函数;(3)不能与参数类型相同的非静态成员函数重名;(4)不能声明为const或volatile;(5)出现在类外的函数定义没有指定关键字static。
映射表类(CMap)是MFC集合类中的模板类,也叫 dictionary ,就像一个只有两列的表,一列是关键字,一列是数据项,两者一一对应。关键字唯一。给定一个关键字,映射表类将快速找到相应的数据项。映射表的查找是以哈希表的形式完成的,所以在映射表中查找数值项非常快。比如公司所有员工都有工号和自己的名字,工号是名字的关键词。如果给出工号,可以很快找到对应的名字。类别映射最适合需要根据关键字进行快速检索的情况。在我们的程序中,我们使用映射表来存储计时器标志值和类实例指针,并使用计时器标志值作为关键字。
从上面的描述可以看出,静态成员函数只能引用一个类中的静态数据成员和静态成员函数。静态成员函数怎么也能引用非静态成员函数和成员变量?这是我们后面要用到的。
分析静态成员函数和非静态成员函数的区别,会发现非静态成员函数之所以能访问所有成员函数和变量,是因为它有一个隐式参数this。在访问成员函数和变量时,它实际上是在前面加了一个引用符号‘this-’,所以我们可以尝试将这个指针作为静态成员函数的参数传递,这样就可以在静态成员函数中访问所有的成员函数和变量?下面是一个实施示例:
Person.h的文件如下:
Class person {public://a本实例的座右铭:CString SZ motion;//用于保存此实例的指针CPerson * pThis//非静态成员函数,弹出该实例的座右铭void get motion();//静态成员函数,弹出该实例的座右铭:Static void getmottostaic(c person * p person);CPerson();virtual ~ CPerson();};
# include person . h c person:c person(){ p this=this;}
CPerson:~CPerson() {}
void c person:get motion(){ AfxMessageBox(SZ motion);}
void c person:GetMottoStaic(c person * pPerson){ pPerson-get motto();}
M _ Person.szMotto=我的座右铭是:这是静态函数访问非静态函数的结果!;m_Person。GetMottoStaic(m _ person . pt this);
其实这个例子在实践中是没有意义的。这样做的目的只是为了演示如何实现这个方法。
Windows提供了一个计时器来帮助我们编写定期发送消息的程序。通常,计时器以两种方式通知应用程序间隔时间已到。
(1)向指定的窗口发送WM_TIMER消息,即给出window类中使用的以下方法。
调用应用自定义的回调函数,即在非窗口类中使用方法。
在窗口类中使用定时器很简单。如果我们想在这个窗户上安装一个电子钟,我们必须每1秒或0.5秒更新一次显示。按照下面的步骤,你可以完成这个电子钟程序,并知道如何使用窗口类中的定时器:
首先,向新项目的主窗口添加一个Label控件来显示时间。然后
用函数SetTimer设置一个定时器,其格式如下:uint settimer (uint nid event,uint nelapse,void(callback export * lpfn timer)(hwnd,uint,uint,dword));
该函数是CWnd类的成员函数,其参数有如下含义:nIDEvent:为设置定时器指定的定时器标志值。当设置了多个定时器时,每个定时器的值是不同的,报文处理功能通过该参数判断是哪个定时器。这里我们将其设置为1;指定发送消息的时间间隔,以毫秒为单位。这里我们设置为1000,也就是一秒;LpfnTimer:指定哪个回调函数执行定时器消息。如果为空,WM_TIMER将被添加到应用程序的消息队列中,并由CWnd类处理。我们在这里将其设置为NULL。最终代码如下:settimer (1,1000,null);
通过类向导给主窗口类添加一个WM_TIMER消息的映射函数,默认值为on TIMER(UINT nIDEvent););
然后就可以把我们的代码添加到OnTimer(UINT nIDEvent)的函数实现中了。参数nIDEvent是我们之前设置定时器时指定的标志值,这里可以用它来区分不同的定时器,做出不同的处理。添加的代码如下:switch(nidevent){ case 1:ctime m _ systime=ctime:getcurrenttime();SetgitemText (IDC _ static _ time,M _ systime . format( % y % M % d % H:% M:% S ));打破;}
代码中的IDC_STATIC_TIME是我们之前添加的标签控件的ID。至此,我们的电子钟的程序已经完成。
在非窗口类中使用计时器需要我们之前介绍过的所有知识。因为是无窗口类,所以不能用窗口类中消息映射的方法来设置定时器。这时候就要用回调函数了。因为回调函数有一定的格式,而且它的参数不能自己确定,所以我们不能用参数来传入这个。但是,静态成员函数可以访问静态成员变量,所以我们可以将此保存在静态成员变量中,指针可以在静态成员函数中使用。对于只有一个实例的指针,这个方法仍然有效。因为一个类中只有一个静态成员变量的副本,所以我们不能用它来区分有多个实例的类。解决方法是使用定时器标志值作为键,类实例的指针作为项,保存在静态映射表中。因为标志值是唯一的,所以它可以快速检索映射表中相应实例的指针。因为它是静态的,回调函数可以访问它们。
二、编程步骤
1.启动Visual C 6.0,生成一个基于对话框的应用程序,将程序命名为‘time demo’;
2.根据程序界面效果图设计对话框。具体设置见代码部分;
3.首先通过类向导创建一个非窗口类,选择泛型类类型,类名为CMyTimer。这个类的作用是每隔一段时间提醒我们做一件事情,然后用这个类创建三个实例,每个实例在不同的时间间隔提醒我们做不同的事情。
4.添加代码,编译并运行程序。
三。程序代码////////////////////////////////////my Timer . h:CMY计时器类的接口。#如果!defined(AFX _ my timer _ H _ _ d 97674d 1 _ B221 _ 49CD _ 9637 _ 4c ba 8 c 3180 ce _ _ INCLUDED _)# define AFX _ my timer _ H _ _ d 97674d 1 _ B221 _ 49CD _ 9637 _ 4c ba 8 c 3180 ce _ _ INCLUDED _ # include AFX templ . H # if _ MSC _ VER 1000 # pragma once # endif/_ MSC _ VER 1000类CMyTimertypedef CMap UINT,UINT,CMyTimer*,CMyTimer * CTimerMapClass CMyTimer {public: //设置定时器,nElapse表示时间间隔,sz表示void setmytimer (uint NELAPSE,CString SZ)进行提示;//销毁此实例的计时器void kill mytimer();//保存该实例的定时器标志值UINT m _ nTimerID//静态数据成员提示的Content CString szContent//静态数据成员,映射表类,用于存储所有定时器信息静态CTimerMap m _ sTimeMap//静态成员函数,用于处理定时器静态void回调mytimerproc的消息(hwnd hwnd,uint umsg,uint id event,dword dwtime);
CMyTimer();virtual ~ CMyTimer();};#endif
///////////////////////////////////////my timer . CPP:cmy timer类的实现。# include STD afx . h # include timer demo . h # include my timer . h # ifdef _ DEBUG # undef THIS _ FILE static char THIS _ FILE[]=_ _ FILE _ _;# define NEW DEBUG _ NEW # endif ctimer map CMyTimer:m _ stime map;
CMyTimer:CMyTimer(){ m _ nTimerID=0;}
CMyTimer:~CMyTimer() {}
void回调CMyTimer:MyTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime){ CString SZ;Sz。Format(%d timer:% s ,I event,m _ stime map[I event]-SZ content);AfxMessageBox(SZ);}
void CMyTimer:setmy timer(UINT nElapse,CString SZ){ SZ content=SZ;m_nTimerID=SetTimer(NULL,NULL,nElapse,MyTimerProc);m _ stime map[m _ ntime rid]=this;}
void CMyTimer:kill my timer(){ kill timer(NULL,m _ nTimerID);m_sTimeMap .移除密钥(m _ ntime rid);}
///////////////////timerdemodlg。h:头文件#如果!defined(AFX _ TIMERDEMODLG _ H _ _ 83d 29 A02 _ A119 _ 4900 _ AB57 _ d6d 011547 f90 _ _ include _)# define AFX _ TIMERDEMODLG _ H _ _ 83d 29 A02 _ A119 _ 4900 _ AB57 _ d6d 011547 f90 _ _ include _ #人。H //由ClassView #include MyTimer.h //由ClassView #if _MSC_VER添加
///////////CTimerDemoDlg对话框类CTimerDemoDlg:public CDialog {//Construction public:CMyTimer m _ my timer 3;CMyTimer m _ my timer 2 CMyTimer m _ my timer 1 cperson m _ PersonCTimerDemoDlg(CWnd * p parent=NULL);//标准构造函数//{ { AFX _ DATA(CTimerDemoDlg)enum { IDD=IDD _ timer demo _ DIALOG };//注意:类向导会在这里添加数据成员//}}AFX_DATA //类向导生成的虚函数覆盖//{ { AFX _ VIRTUAL(CTimerDemoDlg)protected:VIRTUAL void DoDataExchange(CDATA exchange * pDX);//DDX/DDV支持//}}AFX_VIRTUAL //实现受保护:HICON m _ hIcon//生成的消息映射函数//{ { AFX _ MSG(CTimerDemoDlg)虚拟BOOL OnInitDialog();afx _ msg void OnSysCommand(UINT nID,LPARAM LPARAM);afx _ msg void OnPaint();afx _ msg HCURSOR OnQueryDragIcon();按钮1上的afx _ msg void();afx _ msg void on timer(UINT nIDEvent);按钮2上的afx _ msg void();按钮3上的afx _ msg void();按钮8上的afx _ msg void();按钮9()上的afx _ msg void按钮10上的afx _ msg void();afx _ msg void on button 11();afx _ msg void on button 12();afx _ msg void on button 13();//} } AFX _ MSG DECLARE _ MESSAGE _ MAP()};#endif
/////////////timerdemodlg。CPP:实现文件# include 标准传真。h # include 定时器演示。h # include timerdemodlg。h # ifdef _ DEBUG # def NEW DEBUG _ NEW # undef THIS _ FILE静态char THIS _ FILE[]=_ _ FILE _ _;# endif CTimerDemoDlg:CTimerDemoDlg(CWnd * p parent/*=NULL */):CDialog(CTimerDemoDlg:IDD,p parent){//{ { AFX _ DATA _ INIT(CTimerDemoDlg)//注意:类向导将在此添加成员初始化//}}AFX_DATA_INIT //注意,加载图标在win32 m _ hIcon=AFX getapp()-load icon(IDR _ MAINFRAME)中不需要后续的DestroyIcon}
void CTimerDemoDlg:DoDataExchange(CDATA exchange * pDX){ CDialog:DoDataExchange(pDX);//{ { AFX _ DATA _ MAP(CTimerDemoDlg)//注意:类向导将在此添加大单动向和DDV调用//}}AFX_DATA_MAP }
BEGIN _ MESSAGE _ MAP(CTimerDemoDlg,CDialog)//{ { AFX _ MSG _ MAP(CTimerDemoDlg)ON _ WM _ sys command()ON _ WM _ QUERYDRAGICON()ON _ BN _ CLICKED(IDC _ button 1,ON button 1)ON _ WM _ TIMER()ON _ BN _ CLICKED(IDC _ button 2,ON button 2)ON _ BN _ CLICKED(IDC _ button 3,ON button 3)ON _ BN _ CLICKED(IDC _ button 8,ON
BOOL CTimerDemoDlg:OnInitDialog(){ CDialog:OnInitDialog();//IDM_ABOUTBOX必须在系统命令范围内ASSERT((IDM _ about box0x fff 0)==IDM _ about box);ASSERT(IDM _ about box0xf 000);CMenu * psys menu=获取系统菜单(FALSE);如果(pSysMenu!=NULL){ CString strabout菜单;斯特拉布特菜单LoadString(IDS _ about box);如果(!斯特拉布特菜单. IsEmpty()){ psys menu-AppendMenu(MF _ SEPARATOR);pSysMenu- AppendMenu(MF_STRING,IDM_ABOUTBOX,stra about menu);} } SetIcon(m_hIcon,TRUE);//设置大图标SetIcon(m_hIcon,FALSE);//设置小图标返回TRUE//除非将焦点设置到控件上,否则返回真实的
void CTimerDemoDlg:OnSysCommand(UINT nID,LPARAM LPARAM){ if((nID0x fff 0)==IDM _ about box){ CAboutDlg dlgAbout;dlgAbout .DoModal();} else { CDialog:OnSysCommand(nID,lParam);} }
见ctimerdemodlg:onpaint(){ if(isi conic()){ cpaintdc DC(this);//绘制sendmessage(WM _ iconerasebkgnd,(WPARAM) dc的设备上下文getsafehdc()(0);//工作区矩形中的中心图标int cxcon=get system metrics(sm _ cxcon):int cy icon=get system metrics(sm _ cy icon):直线裂纹;getclientrect(矩形):int x=(直线)。宽度()-c C1)/2:int y=(直线)。Height() - cyIcon 1)/2://绘制哥伦比亚特区图示DrawIcon(x、y、m _ hicon);} else { CD dialog:on pan();} }
HC cursor ex merdemodlg:onquerydragicon(){ return(HC cursor)m _ hicon;}
请参见ctimerdemodlg:on button 1(){ m _ person。szlemma=我的座右铭是:奋斗加运气等于成功我的天;m_Person(人员)。get ostatic(m _ person。pthis);}
请参阅ctimerdemodlg:on mer(uint NID事件){ switch(NID事件){ case 1:victim m _ system=victim:getcurrencime();SetDlgItemText(IDC_STATIC_TIME,m _ systime).格式(" %Y年%m个月%d日% h:% m:% s);打断;打断;} cddialog:on mer(NID事件);}
请参见ctimerdemodlg:on button 2(){ settimer(1,1000,null);}
参见ctimerdemodlg:on button 3(){ kill timer(1);SetDlgItemText(IDC_STATIC_TIME,电子钟定时器被关闭了-什么
请参见ctimerdemodlg:on button 8(){ m _ my timer 1 .SetMyTimer(5000,’该起床了我的天);}
请参见ctimerdemodlg:on button 9(){ m _ my timer 2 .SetMyTimer(10000,’该工作了我的天);}
请参见ctimerdemodlg:on button 10(){ m _ my timer 3 .SetMyTimer(15000,’该睡觉了我的天);}
请参见ctimerdemodlg:on button 11(){ m _ my timer 1 .kilmytimer();}
请参见ctimerdemodlg:on button 12(){ m _ my timer 2 .kilmytimer();}
请参见ctimerdemodlg:on button 13(){ m _ my timer 3 .kilmytimer();}
四~我爱你~小结
通过以上的介绍,大家应该知道如何在静态成员函数中访问非静态数据成员和非静态成员函数,并了解了如何在非窗口类中使用定时器。当然这只是解决这个问题的一种方法,相信还有更好的解决办法。这个种方法有一定的灵活性,可以在很多地方用到,例如网络程序中的连接超时以及定时刷新等需要自己来控制,就可以使用这种方法。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。