Visual C++数字图像模式识别典型案例详解
上QQ阅读APP看书,第一时间看更新

1.3.1 Visual C++编程方法

面向对象是一种重要的程序设计方法,采用这一思想的C++是当今世界上应用最广泛的编程语言之一。Windows平台下的C++编程工具首推Microsoft的Visual C++。但是,编写Windows应用程序只熟悉C++的语法还是远远不够的,还必须掌握微软基础类库(Microsoft Foundation Class,MFC)。

1.面向对象编程

面向对象的程序设计的最根本目的就是使程序员更好地理解和管理庞大而复杂的程序。为此,面向对象的程序设计围绕真实世界的概念来组织模型,它采用对象来描述问题空间实体。一般认为,对象是包含现实世界物体特征的抽象实体,它反映了系统为之保存的信息和与它交互的能力,是一些属性和服务的封装体。在程序设计领域,可以用“对象=数据+作用于这些数据上的操作”这一公式来表示面向对象的编程。

对于面向对象的程序设计,一个对象具有状态、行为和标识。

·对象的状态包括它的属性和这些属性的当前值。

·对象的行为包括可以进行的操作以及伴随着的状态的变化。

·对象的标识用来区别于其他对象。

(1)面向对象

面向对象的核心概念就是通常所说的抽象、封装、继承和多态性。

1)抽象

抽象就是忽略一个主题中与当前目标无关的那些方面,以便将注意力更充分地放在与当前目标有关的方面。抽象并不要求了解全部问题,而只选择其中的一部分。抽象包括两个方面:一是过程抽象,二是数据抽象。过程抽象中,任何一个明确定义功能的操作都可被使用者简单地看作单个实体,尽管该操作可能由一系列更低级的操作来完成。数据抽象定义了数据类型以及施加于该类型对象上的操作,并限定对象的值只能通过使用这些操作来查看和修改。

2)封装

封装是面向对象的特征之一,是对象和类概念的主要特性。所谓封装是指隐藏类(class)为支持和实施抽象所作的内部工作的过程,把过程和数据包围起来,对数据的访问只能通过已定义的接口进行。类的接口是公有的,它定义了一个类所能完成的功能,而这些接口的实现是私有的或受保护的,它定义了类完成这些功能的具体操作。对于使用这些类的编程者来说,只需要知道类所能完成的功能即可,而不需要知道这些功能具体是如何实现的。

封装保证了模块具有较好的独立性,使得程序维护修改较为容易。对应用程序的修改仅限于类的内部,因而封装可以将应用程序修改带来的影响降低到最低限度。

3)继承

对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类或者子类,而原始类称为新类的基类或者父类。派生类可以从它的基类那里继承方法和实例变量,并且它可以修改或增加新的方法使之更适合特殊的需要。继承性很好地解决了软件的可重用性问题。比如,所有的Windows应用程序都有一个窗口,它们都可以看作是从一个窗口类派生出来的。但是为实现有的应用程序用于文字处理、有的应用程序却用于绘图的目的,当基类派生出了不同的子类后,需为各个子类增加不同的特性。

下面,举一个使用Visual C++编程的例子。注意,MFC中的CView类封装了Windows中的自定义视窗控件。图1-3所示为CView类的继承结构。

图1-3 CView类在MFC中的继承层次

在图1-3所示中,类CObject是所有的MFC类的基类,其功能有:串行化支持,运行库信息,对象诊断输出以及与集合类的兼容等。类CCmdTarget从类CObject直接派生,它是Microsoft基础类库的消息映射结构的基类,消息映射将命令和消息传递给所编写的处理成员函数。这里,命令指来自菜单项、命令按钮和加速键的信息。类CWnd提供了MFC中所有窗口类的基本功能,它封装了Windows中的窗口句柄hWnd。类CView从CWnd直接派生,它提供了用户自定义视窗的能力,以用于用户和应用程序间信息交互。

事实上,类CView本身仅提供了特定于编辑控件的32个成员函数,但是,用户可以通过类CView进行调用的成员函数却有300多个。这300多个成员函数中的绝大部分由其基类提供,其中CWnd就为CView提供了多达304个成员函数。由于CView类继承了其他基类的数据和方法,因此,可以通过CView类调用CWnd类中提供的方法来实现对标准Windows窗口的操作。

4)多态性

多态性是面向对象程序设计的重要特性之一。它与前面讲过的封装性和继承性构成了面向对象程序设计的三大特性。这三大特性是相互关联的。封装性是基础,继承性是关键,多态性是补充,并且多态性必须存在于继承的环境中。

多态性是指允许不同类的对象对同一消息作出响应,发出同样的消息被不同类型的对象接收时导致完全不同行为的特性。这里所说的消息主要是指对类的成员函数的调用,而不同行为是指不同的实现。比如同样的加法,把两个时间加在一起和把两个整数加在一起是完全不同的。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好地解决了应用程序中不同类间函数同名的问题。用户只需发送一般形式的消息,而将所有的实现交给接收消息的对象。对象根据接收到的消息进行相应的操作。

(2)类定义

类是Visual C++中面向对象编程的一个具体实现方式。它集中体现了面向对象编程的各种特性。

类的定义分为说明部分和实现部分。说明部分是用来实现该类成员的说明,包括数据成员说明和成员函数说明(成员函数是用来对数据成员进行操作的);实现部分是对成员函数的定义。概括说来,说明部分将告诉使用者“做什么”,而实现部分告诉使用者“怎么做”。

类定义的一般格式如代码1-1所示。

代码1-1类的定义

        Class  <类名>
        {
            private:
                <私有数据成员或成员函数的说明(缺省时为私有)>
            protected:
                <保护数据成员或成员函数的说明>
            public:
                <公共数据成员或成员函数的说明>
        };
        <各个成员函数的实现>

其中:

·public表示类的公有部分,是全局透明的。它的数据成员和成员函数是开放的,既可以由本类的成员函数访问,也可以由程序的其他部分直接访问。

·private表示类的私有部分,是完全隐藏的。它只能由本类的成员函数访问,而不允许程序的其他部分直接访问。

·protected表示类的保护部分,介于以上两者之间,是半透明的。它可以由本类的成员函数和派生类的成员函数直接访问。保护部分主要用于类的继承。

(3)对象定义

Class定义的是“类”的类型,具有“类”类型的变量称为类的实例变量,即对象。因此,定义对象之前,一定要先定义好该对象的类。对象的声明和基本类型的声明一样。

1)对象定义的格式

对象定义的格式如下:

        <类名>  <对象名表>

其中,<类名>是特定对象所属的类的名字,即所定义的对象是属于该“类”类型的对象。<对象名表>中可以有一个或多个对象名,定义多个对象名时用逗号隔开。<对象名表>中,可以是一般的对象名,也可以是指向对象的指针名,还可以是对象数组名。

2)对象成员的表示方法

一个对象的成员就是该对象的类所定义的成员。对象成员有数据成员和成员函数,其表示方法如下:

        <对象名>.<成员名>

        <对象指针名>-><成员名>

(4)构造函数和析构函数

构造函数和析构函数是在类体中说明的两种特殊的成员函数。构造函数用于初始化类变量或者分配内存空间。构造函数的定义方法和其他成员函数相同,但是构造函数用类名作函数名,它不能指定返回值类型和显示返回值。每当该类的对象被创建时,编译器都将自动调用构造函数初始化创建的对象。一个类可以有很多个构造函数,当然,这些构造函数的形参各不相同。析构函数与构造函数的功能正好相反,当对象要撤销时,系统将自动调用析构函数来清理对象所占的内存。

2.MFC类

一个完整的Windows应用程序首先必须有用户界面、窗体、各种对话框、按钮、菜单等,开发者可以通过调用Windows的API函数来实现这一切,但是API采用的是C语言的标准,用它编写程序,很多C++的特性(比如继承、封装等)就无法实现。Microsoft也提供了C++库——MFC,它位于任何Windows API之上,能够使开发者更加高效和轻松地工作。

(1)MFC的特点

MFC是Visual C++软件的一部分。MFC库中的所有类形成了建立应用程序的框架,利用这些类可以充分支持Windows应用程序的开发。MFC具有以下特点:

·MFC完整地封装了Windows的API函数,包括消息、控件、窗口函数、菜单、对话框、图形设备接口(GDI)、对象链接以及多文档界面(MDI)等。

·MFC减少了大量在编写Windows程序时必须编写的代码。同时,MFC具有良好的通用性和可移植性,提供了所有C++编程的优点。

·MFC借助Application Wizard使开发者摆脱了那些每次都必写的基本代码,借助Class Wizard和消息映射使开发者摆脱了定义消息处理时那种混乱和冗长的代码段。

·MFC利用C++的封装功能使开发者摆脱了Windows中各种句柄的困扰,他们只需要面对C++中的对象。这样,开发者可更接近编程语言而远离系统。

MFC最主要的优势是它的效率高。

(2)MFC的层次

MFC是很庞大的,其包含了大约200个不同的类。利用它们,开发者可以完成各种标准的Windows编程任务。在设计思想上,MFC将面向对象的方法和事件驱动结合得非常好。但在一般的应用程序中,不需要使用所有的类。事实上,开发者可能只需要使用十多个MFC中的类就可以编写一个非常漂亮的程序。下面着重介绍一下MFC中最常用的几个类。

1)文档类

文档类用来构建应用程序的框架,它的根是CCmdTarget类。它为客户定义的文档提供了大量的操作,比如Open、Close、Save等。文档类主要应用于文档数据的存储、调出以及修改。文档类包括CDocument、CDocItem、CDocTemplate,还有一些它们的子类,使用户能对文档数据进行查询、编辑和修改。

2)视图类

视图类由CView以及它的子类组成。CView类继承自CWnd。CView类是可以在窗口中创建的子窗口,它可以提供一个特殊的、能接收外来输入的结构窗口。CView类包含了常见的一些可视化编辑控件,如CScrollView、CCtrlView、CEditView、CTreeView、CListView。CView类广泛地应用于基于文档的应用程序中,它的许多操作都是和文档相关的,比如拖动滚动条时,看到的文档或图像对应部分的滚动。一个视图只能分配给一个文档,但是一个文档可以拥有多个视图。

3)框架窗口类

框架窗口类为开发者提供创建应用窗体的方法,可以建立单文档与多文档两种窗体。

MFC为单文档界面(Single Document Interface,SDI)、多文档界面(Multiple Document Interface,MDI)准备了CframeWnd、CMDIFrameWnd、CMDIChildWnd等几个类。开发基于文档的应用程序时,框架窗口即通过这些类来实现。

4)对话框类

对话框类主要用于创建及操作各种各样的模态和非模态对话框,其包含了通用对话框类CDialog以及支持类,包括选择颜色、选择字体、查找/替换等通用对话框。CDialog类也是继承自CWnd。

5)控件类

控件类包括常见的Windows控件,这些控件包括静态文本、命令按钮、位图按钮、列表框、组合框、编辑控件等。

6)绘图类及绘图对象类

Windows中所有图形化的输出都是绘制在设备环境(Device Context,DC)的区域内。绘图类以及绘图对象类包括设备环境类与它的一些派生类,提供了诸如字体、颜色、画线、画圆、填充等绘图操作,绘图对象中所包含的类用于模型化绘图对象,如画笔、刷子、字体、位图和调色板等。

7)文件服务类

该类提供了各种用于处理文件的类。常用的文件服务类是CFile类及其派生类,它们控制普通文件的I/O操作,比如读/写文件、重命名及删除等。

8)其他类

MFC还有异常处理、特殊结构(List、Array和Map)、网络服务(WinSock)、对象链接和嵌入(OLE)、数据库(ODBC、DAO)的一些专用类。

为了建立一个MFC应用程序,开发者应该学会直接使用这些类,而且通常需要从这些类中继承新的类。在继承的类中,开发者可以建立新的成员函数,以适应开发需要。

3.程序框架

(1)几种文件类型

在Visual C++中,应用程序是以Project(工程)的形式存在的,工程文件以.dsp为扩展名。扩展名为.dsw的文件称为Workspace(工作空间)文件。在Workspace文件中可包含多个Project,由Workspace文件对它们进行统一协调和管理。

在应用程序中,大量应用的是以.h和.cpp为扩展名的文件。其中以.h为扩展名的文件称为头文件,主要包含的是类的定义。以.cpp为扩展名的文件称为实现文件,主要包含的是类成员函数的实现代码。

在应用程序中经常要使用一些位图、菜单之类的资源,Visual C++中以.rc为扩展名的文件称为资源文件,里面包含了应用程序中要用的所有的Windows资源。

(2)应用程序框架

Windows程序设计方式是一种完全不同于传统DOS方式的程序设计方式,它是一种事件驱动的程序设计方式。在程序提供给用户的界面中,有许多可操作的可视对象。用户从所有可能的操作中任意选择,被选择的操作会产生某些特定的事件,这些事件发生后的结果是向程序中的某些对象发送消息,然后这些对象调用相应的消息处理函数来完成特定的操作。

Windows应用程序的最大特点就是程序没有固定的流程,只是针对某个事件的处理有特定的子流程,Windows应用程序就是由许多这样的子流程构成的。

从上面的讨论中可以看出,Windows应用程序在本质上是面向对象的。程序提供给用户界面的可视对象在程序的内部一般也是一个对象,用户对可视对象的操作通过事件驱动模式触发相应对象的可用方法。程序的运行过程就是用户的外部操作不断产生事件,这些事件又被相应的对象处理的过程。

Windows程序工作原理如图1-4所示。

图1-4 Windows程序工作原理

我们所要建立的MFC应用程序毫无疑问是Windows操作系统环境下的应用程序,但是MFC应用程序又有自己的特点。因为MFC为应用程序做了大量的工作,而且MFC也为应用程序建立了大量的类,所以我们也有一个MFC建立应用程序的框架,如图1-5所示。

图1-5 MFC应用程序框架

图1-5所示中给出了使用MFC方式的应用程序的4个主要类之间的关系,从图中可以看出:

·CMYAPP类:主要的作用是用来处理消息,它统一管理程序收到的所有消息,然后把消息分配到相应的对象。

·CMAINFRAME类:是CMYVIEW的父类,使各种视窗VIEW显示在主框窗MAINFRAME的客户区中。

·CMYVIEW类:作用是显示数据,数据的来源是类CMYDOC。在MFC程序中,程序的数据放在文档中,而显示数据则是利用视窗方式。

·CMYDOC类:作用是管理数据,通过CMYVIEW类和用户进行交互并对所管理数据进行操作。

使用过传统的Windows编程方法的人都知道,在应用程序中有一个重要的函数WINMAIN(),这个函数是应用程序的基础,用户的操作所产生的消息正是经过这个函数进行处理后,再派送到对应的对象中进行进一步处理的。

然而,基于MFC开发的应用程序中,用来处理消息的是系统自动生成的MFC中的CWINAPP类的派生类CMYAPP,由这个类来完成WINMAIN()所完成的工作。CMYAPP执行窗口注册等标准服务,然后再调用应用程序对象中的成员函数来初始化和运行应用程序。

在MFC应用程序设计中,我们所要做的工作主要是设计自己需要的类,编写并处理相关的消息和事件,并将其添加到应用程序中去。

(3)文档、视图和框架

文档/视图结构是Visual C++中使用MFC开发基于文档的应用程序的基本框架。在这个框架中,数据的维护及显示分别由两个不同但又彼此紧密相关的类——文档类和视图类来负责,而框架窗口则是视窗的包容器。框架的核心是文档和视图。一个文档是一个数据对象,用户编辑过程中与这个数据对象进行交互。数据是由File菜单中的New或Open命令创建的,一般都保存在一个文件中。一个视图是一个窗口对象,用户通过这个窗口对象与一个文档进行交互。

在文档/视图结构中,文档的任务是对数据进行管理和维护,数据被保存在文档类的成员变量中。文档通过一个称为串行化(Serialize)的过程将数据保存到磁盘文件或数据库中。MFC类库为数据的串行化提供了默认的支持,只需要在此基础中稍加修改,就可以为自定义的文档类提供串行化支持。文档类还可以处理命令消息。这里所谓的命令消息是指来自如菜单、工具栏按钮和加速键的WM_COMMAND通知消息。除了WM_COMMAND外,文档不能处理其他的Windows消息。

视图类在文档和用户之间起中介作用。视图可以直接或间接地访问文档类中的成员变量,它从文档类中(而不是从存储介质中)将文档中的数据取出来,并在屏幕上显示出来。同时,它可以接收用户的输入,并接受用户的修改,通过调用文档和视图的接口将修改的信息反馈给文档类。实际的数据更新仍然是由文档来完成的。

每一个文档可以有多个视图类的成员函数,但每个视图只能对应于一个确定的文档。也就是说,视图是文档的不同表现形式。可见,文档/视图模式总的工作方式是:视图把文档中的数据以特定的方式显示于计算机屏幕上,用户通过与视图的交互来查看数据并对数据进行修改。然后,视图通过相关联的文档类的成员函数将经过修改的数据传递给文档对象。文档对象获得修改过的数据后,对其进行必要的修改,最后保存到永久介质(如磁盘文件)中。

文档/视图模型的一个重要机制是文档、视图及框架的关联性,三者互相协调,互相包含指向对方的指针。这种关联是通过一种称为文档模板的类来完成的。

程序开始运行时,将调用CMYAPP类InitInstance成员函数来完成程序的一系列初始化工作,其中一段程序如代码1-2所示。

代码1-2框架程序的初始化

        CsingleDocTemplate *pDocTemplate;
        pDocTemplate=new CSingleDocTemplate(
            IDR_MAINFRAME,
            RUNTIME_CLASS (CMySDIDoc),
            RUNTIME_CLASS (CMainFrame),
            RUNTIME_CLASS (CMySDIView));
        AddDocTemplate(pDocTemplate);

CSingleDocTemplate类即单文档模板类,它首先定义了一个单文档模板类的指针,然后动态创建一个单文档模板类的实例,该实例中将特定的文档类(CMySDIDoc)、视图类(CMySDIView)和框架类(CMainFrame)绑定在一起,最后调用AddDocTemplate()函数,将这个模板实例添加到应用程序的文档模板列表中。