C++类

2024-10-05

C++类(共3篇)

C++类 篇1

1类图的概念

类是一种用户自定义的类型, 它和基本类型, 如浮点型、整型, 有类似的特征。同样, 我们也可以声明某个类类型的变量, 这个变量就称为类的对象, 声明对象的过程叫做类的实例化。类和基本类型的区别在于, 类类型中同时包含了对数据进行操作的函数。

类的成员可以有public (公有) 、protected (保护) 和private (私有) 三种访问控制属性, 外界只能访问类的公有成员。为了形象的描述类, 笔者根据类封装的特性在教学过程中设计了一个称为“类图”的图形。如图1所示。

外框 (1) 表示类是数据成员和函数成员的封装。箭头 (2) 表示外界能访问类的公有成员。方框 (3) 包含类的公有成员, 表示类的外部接口。小箭头 (4) 表示类的公有成员可以访问类的保护和私有成员。方框 (5) 包含类私有和保护成员, 表示类私有和保护成员外界不能直接访问。

通过类图我们可以很清楚的看到类成员的访问权限。

2类的继承和派生

面向对象的程序设计提供了类的继承机制, 该机制自动地为一个类提供来自另一个类的操作和属性, 这使得程序员只需要在新类中添加已有类中没有的成员来建立新类。以原有的类为基础产生新的类, 我们就说新类继承了原有的类, 或者说从原有类派生出新类。在这个过程中, 原有的类我们称为基类, 新类我们称为派生类 (或称为父类和子类) 。

在C++中, 声明派生类的一般形式为:

在C++程序设计中, 生成一个类的派生类, 需要指定基类的类名, 继承方式和新增加的成员。继承方式规定了派生类中从基类继承的成员的访问控制权限。继承方式有公有继承、保护继承和私有继承三种方式, 其关键字分别为public、protected和private。派生类成员指除了从基类继承来的所有成员之外, 自己扩充的数据和函数成员。

例如已经声明一个学生类Student, 下面的语句声明了一个从学生类派生而来的研究生类Graduate Student:

派生类由基类除构造函数和析构函数以外的全部成员和派生类新增成员组成。那么基类成员在派生类中的访问控制属性如何呢?这个由派生时的继承方式决定呢。

下面我们将讨论不同继承方式时派生类中基类成员的访问控制属性, 并绘制派生类的“类图”。

3继承方式

在这里我们首先定义一个基类Point (点) 类。Point (点) 类有两个数据成员:x坐标和y坐标。

Point类用类图表示如下:

由Point类派生出新类Circle (圆) 类。圆是由圆心和半径构成的, 圆的圆心具备了Point类的全部特征, 同时圆自身也有一些特点, 比如有半径等。我们希望能够访问圆心的坐标, 能够获得半径的大小。

接下来我们讨论对于三种继承方式, Circle (圆) 类分别需要怎样如何设计才能达到上述要求, 并画出类图。

1) 公有继承

当类的继承方式为公有继承的时候, 基类的public (公有) 和protected (保护) 成员在派生类中访问权限不变, 而基类的private (私有) 成员在派生类中不可直接访问。

公有继承的派生类类图如下所示:

[例1-1]Point类的公有继承。

这里派生类Circle继承了基类Point, 因此派生类Circle吸收了基类Point除默认的构造函数和默认的析构函数以外的所有成员。继承方式为公有继承, 所以基类中的公有成员和保护成员在派生类中访问属性保持原样, 基类中的私有成员在派生类中不可直接访问。

Circle类中继承的获得圆心坐标的函数 (Get X () 和Get Y () ) 访问控制属性是公共的, 所以只需添加获得半径的函数Get R () 即可。

公有继承后的Circle类用类图表示如下:

Circle类继承了Point类的成员, 实现了代码的重用, 同时通过添加新的成员, 加入了自身的特性, 实现了代码的扩充。公有继承是类的继承中用的最多的继承方式。

2) 私有继承

当类的继承方式为私有继承的时候, 基类中的public (公有) 和protected (保护) 成员被吸收后成为派生类的私有成员, 而基类的private (私有) 成员在派生类中不可直接访问。

私有继承的派生类类图如下所示:

我们将例1-1中的继承方式改为私有继承。派生类Circle继承了基类Point, 因此派生类Circle吸收了基类Point除默认构造函数和默认析构函数以外的所有成员。继承方式为私有继承, 所以基类中的公有成员和保护成员在在派生类中都变成了私有成员, 基类原来的对外接口全部被隐藏, 外部使用者不能通过派生类对象访问基类原来的任何成员。派生类的新增成员可以访问从基类继承过来的公有成员和保护成员。

[例1-2]Point类的私有继承。

经过私有继承以后, 从基类继承的成员都变成了派生类的私有成员或不可直接访问的成员, 如果进一步派生的话, 基类全部成员就无法在新的派生类中直接访问。因此, 私有继承之后, 基类的成员就无法在以后的派生类中直接发挥作用, 相当于终止了基类的功能, 为了保证基类的原来的对外接口特征在派生类中也存在, 常常在派生类中重新声明同名的成员。

本例中, 私有继承后, 基类的成员函数Get X () 、Get Y () 和Move () 均变成了派生类的私有函数成员, 外界不能直接访问。为了在派生类中仍能访问圆心坐标, 我们可以在Circle类中添加以上三个函数成员。则派生类的声明可以改为:

公有继承后的Circle类用类图表示如下:

Circle类中继承的获得圆心坐标的函数 (Get X () 和Get Y () ) 访问控制属性是私有的, 派生类对象不能直接访问, 所以在新增成员除了需要添加获得半径的函数Get R () , 还需要添加获得圆心坐标的函数 (Get X () 和Get Y () ) 。

3) 保护继承

当类的继承方式为保护继承的时候, 基类中的public (公有) 和protected (保护) 成员被吸收后成为派生类的保护成员, 而基类中的private (私有) 成员在派生类中不可直接访问。

保护继承的派生类的类图如下所示:

我们将例1-1中的继承方式改为保护继承。派生类Circle继承了基类Point, 因此派生类Circle吸收了基类Point除默认构造函数和默认析构函数以外的所有成员。继承方式为保护继承, 所以基类中的公有成员和保护成员在在派生类中都变成了保护成员, 基类原来的对外接口全部被隐藏, 外部使用者不能通过派生类对象访问基类原来的任何成员。派生类的新增成员可以访问从基类继承过来的公有成员和保护成员。

[例1-3]Point类的保护继承。

经过保护继承以后, 从基类继承的成员都变成了派生类的保护成员或不可直接访问的成员。因此, 保护继承之后, 基类的成员就无法在以后的派生类中直接发挥作用, 为了保证基类的原来的对外接口特征在派生类中也存在, 常常在派生类中重新声明同名的成员。

本例中, 保护继承后, 基类的成员函数Get X () 、Get Y () 和Move () 均变成了派生类的保护函数成员, 外界不能直接访问。为了在派生类中仍能访问圆心坐标, 我们可以在Circle类中添加以上三个函数成员。则派生类的声明可以改为:

保护继承后的Circle类用类图表示如下:

Circle类中继承的获得圆心坐标的函数 (Get X () 和Get Y () ) 访问控制属性是保护的, 派生类对象不能直接访问, 所以在新增成员除了需要添加获得半径的函数Get R () , 还需要添加获得圆心坐标的函数 (Get X () 和Get Y () ) 。

4) 私有继承和保护继承的区别

从类图上看, 类保护继承和私有继承似乎一样:基类私有成员在派生类中不能直接访问, 基类的公有成员和保护成员可以被派生类新增成员访问, 外部使用者不能通过对象直接调用基类它们。但是, 当以派生类作为新的基类继续派生的时候, 二者的区别就出现了。

假设Point类分别以私有和保护两种继承方式派生出Circle类, 再在Circle类的基础上以公共继承的方式派生出圆柱体类Cylinder。仔细分析两种条件下的圆柱Cylinder类的成员, 我们会发现Point类的公共成员在Cylinder类中访问控制属性不同。

摘要:继承是C++面向对象程序设计中的重要概念, 也是类的特性之一。通过继承可以循序渐进的接近对象的本质, 同时也在编程中提高了代码的重用率, 受到广泛的运用。笔者在教授C++程序设计的过程中, 针对类的特性, 绘制了类图, 并应用类图来讲授类继承的三种方式。有图形的配合, 形象生动, 降低了学生的理解难度, 收到学生的欢迎。

关键词:类,类图,继承,对象,重用率

参考文献

[1]侯俊杰.深入浅出MFC[M].武汉:华中科技大学出版社, 2003.

[2]钱能.C++程序设计教程[M].北京:清华大学出版社, 1999.

[3]郑莉, 董渊.C++语言程序设计[M].北京:清华大学出版社, 2001.

[4]萨师煊, 王珊.数据库系统概述[M].3版.北京:高等教育出版社, 2002.

[5]Date C J.数据库系统导论[M].7版.北京:机械工业出版社, 2000, 10.

一种用户友好的C++串口类设计 篇2

1 目前Windows操作系统上进行串口编程的四种方法

1) 使用微软C运行时库的I/O函数

可采用_inp, _inpw, _inpd, _outp, _outpw, _outpd等函数对串口操作, 这些函数可直接对串口芯片进行操作。需要对串口硬件电路的工作原理和时序非常熟悉, 才能正确使用它们进行串口的设置、读取以及写入的操作。

2) 使用Windows API函数

可采用Windows API中的Read File, Write File, Build Com DCB, Set Comm Break, Set Comm Config等函数对串口操作。这些函数用法复杂, 不易掌握, 需要对Windows系统的文件以及设备管理深入了解才能正确使用。

3) 采用第三方串口通信类

互联网上有一些源代码开放的第三方串口通信类, 比如CSerial Port, cn Comm等, 采用特定的语言如C++语言编写, 并且打包成类 (class) , 内部通过调用Windows API实现对串口的操作。它们突出的优点在于函数接口简单明了, 易于编程使用, 缺点是仅能供该特定语言的程序员编程使用, 而且质量良莠不全, 需要认真测试和挑选。

4) 使用MFC库附带的Active X控件MSComm

这是微软公司提供的用于串口通信控制的Active X控件 (Microsoft Communication Control) 。它支持编译期以图形化的方式来设置串口的属性, 它还提供了53个成员函数接口, 用户可以通过在程序中调用这些接口来设置串口的属性以及进行串口通信。

从表1中可以看出, 编程难度和调试难度的正相关性比较大, 这是因为使用的函数越低级, 控制的粒度越细, 编程和调试的难度和工作量越大。与第三方串口通信类相比, MSComm面向多种语言, 所以使用MSComm控件进行编程的情况比较普遍。

然而对于刚刚接触串口编程的用户来说, 使用MSComm控件也不容易。首先, 该控件接口函数高达53个, 短时间不容易弄清楚它们的用法;其次, 该控件采用VARIANT类型的接口数据, 这种数据结构比较复杂, 不易掌握;第三, 该控件产生的串口事件是用不同的无符号整数值表示, 用户需要查找资料来确定该整数值表示的意义, 不够直观。因此在MSComm的基础上进一步简化串口编程就成了本文设定的工作目标。

2 用户友好的串口DLL和C++串口类设计

2.1 串口DLL的设计

MSComm控件相当稳定可靠, 为本文的设计工作提供了一个稳固的基础。MSComm是一种Active X控件, 必须将它放置在Active X容器中才能工作。对话框是常用的Active X容器。为此本文设计了一个对话框类Comm Ctnr, 当在对话框类Comm Ctnr中插入MSComm控件时, Visual C++开发环境自动替MSComm控件生成代理类CMSComm, 该文将CMSComm类的对象作为对话框类Comm Ctnr的数据成员。

用户可以直接将相关的.cpp和.h文件以及.rc资源文件拷贝到自己的工程目录下, 但这种方式显然很不方便。更好的办法是将相关的文件独立编译成一个动态链接库 (DLL, Dinamic Link Library) , 用户只需要调用这个动态链接库就可以进行串口操作。MFC (Microsoft Foundation Class Library) 支持两种动态链接库, 一种是MFC Library Extension DLL, 一种是MFC Library Regular DLL。前者能够将整个C++类作为DLL的外部接口, 这样减少了DLL实现的困难, 然而要求客户程序使用C++语言编写, 并且动态链接MFC库才能够调用这种DLL, 这样就大大缩小了它可能的用户群, 所以本文不采用这种方式。第二种方式是使用MFC Library Regular DLL方式, 这种DLL可供多种编程语言设计的客户程序使用。由于它对外界的接口只允许是C风格的函数, 因此它不能接受类 (class) 类型的参数。为此本文设计每个C风格函数接口都需要一个无符号整型的参数代表串口号, 串口号和串口是一一对应的, 在DLL内部, 串口号被转换为相应的MSComm串口控件。只要提供串口号就可以对相应的串口进行操作, 这对于用户是十分方便的。比如, 以下语句初始化并且打开一个串口 (num为串口号) :

if (__init (own, num, rcv, snd, othr) ) __open (num, settings.c_str () , rthreshold, sthreshold) ;

2.2 用户友好的消息机制设计

消息传递是Windows程序与程序之间以及程序内部进行交互的主要方式, 该文采用自定义的消息来完成客户程序与DLL之间的交互。该文为DLL设计了三种消息, 一旦MSComm控件有消息发出, 将会被转换成这三种消息之一发送给客户程序。一种是接收数据消息:该消息通知客户程序, 串口有数据到达;第二种是发送数据消息:该消息通知客户程序, 串口上一批数据已经发送完, 需要客户提供下一批待发送的数据;第三种是硬件控制消息或者是串口错误, 比如串口clear-to-send线电平发生变化, 奇偶校验错误等等。与直接使用MSComm控件相比, 消息被清晰的分成三类, 客户程序可以选择响应或者不响应哪类消息, 而且第三类消息带有简要的文本, 用户可以通过这个文本知道串口到底发生了什么问题, 有利于用户调试程序。

消息的具体数值由客户程序来定义, 在串口初始化的时候, 将消息值传递给DLL。将来有相应的串口事件发生时, DLL就会将这些消息发送给客户程序。比如, 下面的语句表明客户程序需要响应接收数据消息和硬件控制消息 (或者串口错误消息) , 但不需要响应发送数据消息:

m_port2.init (Get Safe Hwnd () , number, WM_COMMY_RCV, 0, WM_COMMY_OTHR) ;

其中, WM_COMMY_RCV表示接收数据消息, WM_COMMY_OTHR表示硬件控制消息和串口错误消息, 这两个消息的数值都是客户程序自己定义的;m_port2是后文提到的C++串口类。

2.3 用户友好的C++串口类设计

客户程序可以直接使用DLL的导出函数来进行串口编程, 这些函数是C风格的函数。对于C++程序员, 该文设计了一个C++串口类Mcomm, 它是DLL导出函数的包装类 (wrapper class) 或者称为代理类 (delegate class) [4]。客户程序通过Mcomm操作串口可以发挥充分发挥C++语言面向对象的优点, 相对于C风格接口, 它的接口更简单, 它还能自动管理串口资源的获取和释放, 减轻了客户程序的负担, 使得串口编程更简洁方便, 体现了简洁、清晰、易用的设计哲学。使用Mcomm时只需要像普通的C++类一样, 将mcomm.h文件包含在项目中即可。

Mcomm类定义如下:

//方便友好的C++串口类Mcomm

class Mcomm

{unsigned num;//串口号

HWND owner;//接收串口消息的窗口的句柄

unsigned rcv, snd, othr;//串口消息

bool opn;//串口打开与否

bool rdy;//串口初始化与否

//禁止的操作

Mcomm (const Mcomm&) ;

Mcomm&operator= (const Mcomm&) ;

public://接口函数

//缺省构造函数

Mcomm () :owner (0) , num (0) , rcv (0) , snd (0) , othr (0) , opn (false) , rdy (false) {}

//串口初始化, 在串口打开之前调用

bool init (HWND own, unsigned n, unsigned rcvevnt=0, unsigned sndevnt=0, unsigned othrevnt=0) ;

bool open (const string&settings, unsigned rthreshold, unsigned sthreshold) ;//打开串口

void release () ;//释放串口资源以使其他程序可以使用该串口

bool close () ;//关闭串口

unsigned read (vector&v) ;//读取串口数据

unsigned write (const vector&v) ;//向串口写数据

~Mcomm () ;//析构函数, 自动释放串口资源

……};

3 结束语

本文基于微软公司的Active X控件MSComm设计了一个串口通信DLL库, 在此基础上设计了一个C++串口类, 提供了一种对用户友好的串口编程途径, 大大降低了串口编程的难度。程序员只需要最低限度的关于串口的知识, 就可以用它来进行串口编程, 这对非专业程序员和初学编程的人员非常有帮助。具有一定水平的程序员可以在此基础上实现更为复杂的串口操作, 比如某种可靠的串口数据传输协议。该DLL库面向多种编程语言, 使得非C/C++程序员也能够使用它来进行串口编程。该DLL库及C++串口类遵循源码开放的原则, 有兴趣的读者可以与笔者联系取得本文的源代码。

参考文献

[1]李现勇.Visual C++串口通信技术与工程实践[M].北京:人民邮电出版社, 2003.

[2]Kruglinski D J.Programming Visual C++.Microsoft Press, 1998.

[3]高传善.接口与通信[M].上海.复旦大学出版社, 1992:10.

C++类 篇3

关键词:类,对象,成员,初始化,访问

0 引言

面向对象程序设计(Object-Oriented Programming Object-Oriented Programming,简称OOP)是在吸收软件工程领域有益概念和有效方法基础上发展起来的一种软件开发方法,是一种以对象为基础,以事件或消息来驱动对象执行相应处理的程序设计方法。对象是面向对象程序设计方法中的基本单位,是对问题域中客观存在的事物的抽象,它是一组属性和作用于这些属性上的操作的封装体;类(Class)则是对象的集合体,将对象进行分类,对具有共同特征的对象进行抽象,形成对这些对象的抽象描述——类,而每个对象就成为该类的一个实例。

在面向对象程序设计语言中,一个类是就是一种数据类型,在定义类的任何对象前必须先给出这个类的声明。标准的C++定义了一些内建的类,例如string。通过类声明(Class Declaration)可以创建一个类,而且可将这个类作为一个数据类型来使用。类声明描述了封装在该类中的数据成员(data member)和成员函数(function member),其中对类数据成员的访问是面向对象程序设计中使用类的一个重要环节。C++是由C语言发展而来,它不仅对C语言的功能进行了扩充,而且增加了面向对象程序设计机制。本文以C++语言为例研究类成员的初始化和访问。

1 类成员的初始化

当定义一个变量时,可在定义的同时完成初始化操作,如:“int sum=0;”。类的对象也是如此,因为对象的意义表达了世界的实体,所以建立对象必须有一个有意义的初始值,而在定义一个类时,定义的是一种数据类型,并不是对象,对象的初始化就不能像变量初始化那样简单,不能在类定义中直接初始化,而应根据不同的情况采用不同方法。

1.1 使用构造函数

因为类声明只是定义了一种类型,并不代表一个对象的定义,所以无法在类声明时对类成员进行初始化。类对象初始化的任务可由类的成员函数完成,但由于成员函数需人为编写代码去调用,这样实现的方式并不理想,最好由系统去自动调用,这就可以使用构造函数。

构造函数是一种与类名相同的成员函数,当创建类的一个实例时(例如,定义一个类的变量时)就会自动调用某个合适的构造函数。

例如:在Date类中定义这样一个函数:

其中,Date(int m,int d,int y)为构造函数,当通过“Date today(10,26,2012);”创建Date类对象today时,系统自动调用构造函数Date(int m,int d,int y),完成Date类对象today成员month、day和year的初始化,获得初始值分别为:10、26、2012。

构造函数的定义可在类定义体内,也可在定义体外,在外部时应加上类作用域符号“∷”。定义构造函数有其规定:函数名必须和类名相同、函数无返回值、函数的调用是在创建对象时系统自动调用而且必须是公有成员。

1.2 通过成员初始化参数表

常量在定义时就必须赋值,如何对类的常量数据成员进行初始化,这是类成员初始化经常遇到的一个问题。因为类是一个抽象的概念,并不是一个实体,并不含有属性值,而只有对象占有一定的空间才具有确定的值,所以不能在类的定义中用赋值语句对常量进行初始化。

例如:试图在对象创建时通过构造函数对常量数据成员进行初始化。

上述初始化方法存在很明显的错误。因为常量一旦定义后是不允许被赋值的,不能对常量PI赋值。常量的初始化应在构造函数进入之后,对象结构已经建立,数据成员已经存在,但还没有执行花括号中的函数体语句时完成,也就是在构造函数名后,花括号之前进行。

上面的构造函数可定义如下:

其中,PI(3.14159)就是C++为类的构造函数提供了一种特殊的方式——成员初始化参数表,它用于对类的数据成员进行初始化,放在构造函数的函数头后面、函数体之前。

成员初始化表中的成员主要包括:常量成员、对象成员、引用成员、基类成员,也可以是一般的数据成员,但不能初始化静态成员。

1.3 静态数据成员的初始化

当需要类的所有对象在类范围内共享某个数据时,应将该数据声明为static的类成员,称之为静态数据成员。

例如:在类的定义中声明:

在Student类的定义中声明了一个静态数据成员count,由于类定义时并不实际分配空间,而静态数据成员要实际地分配空间,因而静态数据成员count的初始化不能直接在类中进行。又因为静态数据成员不是某个具体对象的成员,同一个类中的所有的对象都共享该变量,不会随着对象的产生而建立,所以它的初始化也不能在构造函数中进行,必须在全局区进行,在所有的函数体外进行初始化。这样在类的内部对静态变量说明,而在外部对它进行定义,这不同于普通的数据成员,必须在外部进行定义,“int Student::count=0;”。

2 类成员的访问

在C++程序设计中,类的成员访问控制有三种方式:

public:公有,成员可以为任意函数所访问。

protected:保护,成员只能为该类的函数所访问。

private:私有,成员只能为该类的成员函数以及该类的派生类中的成员函数访问。

对类数据成员进行访问,应根据不同的情况采用不同的方式。

2.1 通过对象以圆点操作符“.”直接访问

当类的成员定义为public时,表明成员是公开的,这样定义的成员在程序中可用圆点操作符“.”直接访问。例如:

其中,数据成员month、day为公有的数据成员,对其访问即可采用“.”直接访问。如:

另外也可以通过指向对象的指针或对象的引用访问。如:

因为类的数据成员一般是不公开的,这些信息应该对外隐藏,才能体现面向对象的封装性,所以一般将数据成员定义为私有和保护成员,而表示类的接口的成员函数一般定义为public的。数据成员的声明如下:

若是类中的私有和保护成员,则不能使用对象直接访问的方式。例如:date1.year=2012;是错误的。

2.2 通过成员函数访问

当类的数据成员定义为private或protected的,应通过成员函数访问。

在类中定义成员函数:

对于类中的私有成员month、d、year可通过如下的成员函数访问方式:

date1.Set Date(10,26,2012);

2.3 通过友员函数访问

由于对象的私有数据和成员函数的细节被封装起来了,使得对象内部的代码和数据受到了保护,其他的对象不能直接修改,实现了对象的封装机制。但是考虑到不同类的成员函数之间、类的成员函数与一般函数之间数据的共享机制,一个普通函数或者其他类的成员函数可以访问到封闭于某一个类中的数据,这在一定程度上破坏了对象的封装性,但却可能大大提高程序的效率,加强了函数之间,类与类之间的联系。

在类中声明一个普通函,标上关键字friend,就成了该类的友员,可以访问该类的一切成员。例如:

在上面例子中将普通函数print Date声明为类的友员函数,则在print Date函数中通过对象名访问了类的私有成员。

3 总结

面向对象程序设计的思想和方法是软件设计、开发和维护技术的一次革命,作为这种技术代表的C++语言得到广泛的认可和应用,而类和对象的使用是C++中最基本的知识,因类具有抽象性和封装性等特点,使得类成员的访问及初始化受到一定的限制,正确地理解和运用对提高面向对象C++程序质量和执行效率具有重要的意义。

参考文献

[1]钱能.C++程序设计教程.北京:清华大学出版社,2009.7

[2]皮德常.C++程序设计教程.北京:机械工业出版社,2009.3

【C++类】推荐阅读:

上一篇:中国动画建筑下一篇:思想政治工作新高地

本站热搜

    相关推荐