今天是我学习 Visual C++ 开发技术的第二天,我主要学习的是类和对象。在学习类和对象之前,首先需要了解两个概念。
面向过程程序设计 ( Procedural Programming ) 方法是结构化的程序设计方法:数据与数据的操作分离,要求将数据发送到过程和函数。
在面向过程程序设计中,算法和数据结构是编程的灵魂。若失掉了算法和数据结构,就把一切都失掉了。( 程序 = 数据结构 + 算法
)
面向对象程序设计( OOP: Object-Oriented Programming ) 方法是目前程序设计的主流方法:数据和作用于这些数据的操作放置在单一的实体内,这个实体就成为对象(object)。
OOP的方式能直接反应出现实世界,程序中所有对象都与某些属性和活动相关联。使用对象大大提高了软件的可重用性,使程序更容易开发和维护。该方法很关键的一点是用对象的思想来思考问题,程序可看作一组相互协作的对象的集合。( 程序 = 对象 + 对象
+ ... + 对象 )
一个对象的状态 ( State ) 用数据域 ( Data Field ) ( 也称为数据成员或属性 ( Properties ) ) 及它们的当前值 ( 状态 ) 来表示。
例如对于 “人” 这个对象,可以用 “眼睛” 、“鼻子”、“耳朵”、“嘴巴” 这些数据域 ( 属性 ) 和当前的状态 ( 健康或生病 ) 来表示。
一个对象的行为 ( Behavior ) 由一组函数 ( 方法 ) 定义。对一个对象调用一个函数就是请求对象执行一个任务。
例如对于 “人” 这个对象,可以由 “跑步”、“吃饭”、“睡觉” 这些函数 ( 方法 ) 定义。我们可以调用 ( 执行 ) “人” 这个对象的 “跑步” 函数 ( 方法 ) 。
类型相同的对象可以用一个类来定义,一个类 ( Class ) 是一个模板或一个蓝图,它定义了对象具有什么样的数据 ( 属性 ) 和函数 ( 方法 ) 。
一个对象就是类的一个实例 ( Instance ) ,创建一个实例的过程称为实例化 ( Instantiation ) 。在对象创建 ( 实例化 ) 后,系统会在内存中(Stack区)为对象分配内存空间,此时只为数据成员分配内存空间。
例如,“鸭”、“狗”、“猫” 这些对象都是动物,可以用 “动物” 这个类来定义;“玫瑰”、“牡丹”、“蓝莓” 都是植物,可以用 “植物” 类来定义。
类的定义由 类的声明 和 实现 两部分组成,一般将类定义的声明部分放在一个头文件 ( *.h ) 中,而将实现部分放在源文件 ( *.cpp )
中。
class 是定义类的关键字。<类名> 是标识符,用于惟一标识一个类,一般以“C”开头,以区别其他标识符。 <类名> 是用户定义的数据类型名,
一对大括号内是类的说明部分,说明该类的所有成员。
class <类名>
{
成员访问限定符( Memberaccess Specifier )
public:公有成员 ( 可被程序中的任何代码访问 )
<公有数据成员和成员函数>;
protected:保护成员 ( 可被本类的成员函数和友元函数访问,也可被本类的派生类的成员访问 )
<保护数据成员和成员函数>;
private:私有成员 ( 只能被本类的成员函数及友元类的成员函数访问。类中的成员若不特别声明 ( 缺省 ) ,都将被视为私有类型 ) 。
<私有数据成员和成员函数>;
}
定义类时应注意的事项
1) 在类声明内不允许对其数据成员进行初始化:因为类是一个数据类型,定义后并没有存储空间,因此不能对数据成员进行初始化(但静态数据成员可在类外进行初始化)。
2) 类中的数据成员的类型可以是任意的,包括整型、浮点型、字符型、数组、指针和引用等,也可以是对象 ( 另一个类的对象可以作为该类的成员,但自身类的对象是不可以的 ) 。
3) 一般在类中先说明公有成员,后面说明私有成员。
4) 类的声明 ( 定义 ) 为一个声明语句,其后必须加分号 " ; " 。
类的作用域
类的作用域指类定义范围内,包含类的声明与类的实现两部分,在此范围内成员之间可直接相互访问。
由于数据成员和成员函数都是类的组成部分,因此成员函数是可以直接访问数据成员的,不用通过参数进行传递。由此可知,在面向对象程序设计语言中,数据和函数是组合在一起的。
类和对象的关系
对象是类的实例,可看作 "类" 类型的变量,但对象又不是普通的变量,对象是数据和操作的封装体。封装的目的就是阻止非法的访问,因此对象实现了信息的隐藏,外部只能通过操作接口/公有成员访问对象数据。对象属于某个已知的类,必须先定义类,然后才能创建对象。
对象的创建形式为:类名 对象名列表;对象可以是一般的对象名,也可以是指向对象的指针名或引用名,也可以是数组。
创建对象后,可以使用点运算符 “.” (对象成员访问运算符 ( Object Member Access Operator ) ),来访问对象的数据成员或调用对象的成员函数。例如:对象名.成员函数名 或 对象名.数据成员。
当然啦,我们也可以通过 对象的指针指向对象的地址:对象名->成员函数名
或 对象名->数据成员。
类的抽象(Classabstraction)就是将类的实现和它的使用分离开来。类的创建者提供了类的描述,使用户了解如何使用类。在类之外可以访问的数据成员和成员函数以及对这些成员的预期行为的描述,一起构成了类的约定(Class’s
Contract)。
类的封装(Classencapsulation)是指类的使用者无需了解类是如何实现的,类实现的细节被封装起来,对用户是隐藏的。
例如:一台个人电脑由很多部件构成,如CPU,CD-ROM,主板等。每个部件可看作一个对象,有自己的属性和功能。为了使这些部件协同工作,我们所需要知道的全部信息就是这些部件如何使用以及如何交互,而无需知道它们内部是如何工作的。部件的内部实现是封装的,对我们来说是隐藏的。在不了解部件如何实现的情况下也能组装出一台计算机。
看着上面一堆文字是不是感觉像是在看天书,啥也看不懂呢~ 让我们用实例来慢慢学习吧~
首先找到我们昨天做的项目 ,双击 "项目名称.dsw" 打开我们做好的项目。
或者可以先打开 Visual C++ 点击 File -> Recent Workspaces 找到我们的项目后点击打开也一样。
首先,我们先点击 Class 切换到类视图,然后右键点击 "项目名称 classes" 选择New Class 新建一个类。
Class Type (类的类型) 我们选择 Generic Class ( 通用类 ) (下拉列表第二个) 。第一个是MFC类,我们现在暂时还不懂那些玩意。
点击OK,这样我们就新建了一个动物类 ( CAnimal ) 。之后呢,我们双击 CAnimal 类,打开它的头文件。
然后在 Public ( 公有成员 ) 下空白的地方,我们新建3个CString ( 字符串 ) 类型的类成员变量 Duck、Dog 和 Cat。(用逗号分隔)
之后,我们双击 CAnimal() ( 构造函数,也是一个类的入口。负责初始化类成员 ) 切换到源文件视图。( 关于构造函数,我们明天具体再说啦~ )
然后我们在 CAnimal() 里分别对 CAnimal 的 3 个类成员进行赋值 ( 初始化 ) 。 PS:使用等号对变量赋值,别错写成 "==" 哦~ 那就变成判断啦~
这样子,我们的 CAnimal 类就完成啦~ 但我们要怎么样来使用写好的类呢?
首先,我们找到该类源文件里包含该类头文件的语句 ( #include "类名.h" ) 通常在该类源文件的最上面~
之后,我们复制这条语句,把这条语句粘贴到我们需要使用的地方 ( 可以是源文件,也可以是头文件中 )
有时候我们会发现,我们的类莫名其妙的 “失踪” 了!咦,我们的类跑到哪里去啦? ( CFristProjectDlg 类失踪啦! )
哈哈,其实这是 VC++ 6.0 的一个BUG,要找回我们的类,只需要点击 FileView 切换到文件视图,找到我们 “失踪” 类的头文件( "类".h ),然后把鼠标移动到类成员变量的末尾位置,点击一下,之后回车。然后再点击 ClassView 切换回类视图。 哈哈,我们 "失踪" 的类又回来啦~
粘贴好了之后呢,我们就可以在需要的地方使用我们写好的类啦~ 但要怎么用呢?
首先呢,我们在需要调用的地方定义一个我们需要使用的类的对象。 例如 CAnimal m_pAnimal;
创建对象后,就可以使用点运算符 “.” (对象成员访问运算符 ( Object Member Access Operator ) ) 来访问对象的数据成员。
按下 F7 编译之后按 F5 执行 ( 调试 ) 我们的程序。
点击 “确定” 按钮之后就可以看到弹出的消息框啦~
这样我们就成功在 CFirstProjectDlg 类中使用了我们写的 CAnimal 类的类成员变量 Duck 的值啦~
对啦,在上面的图片中使用了注释,注释是什么东西呢?为了方便解读代码,我们有时候需要对代码进行注释。
注释有两种,分别是行注释和块注释。行注释呢,用双斜杠 ( // ) 表示。在双斜杠后面的,该行内所有内容均被视为注释。
块注释呢,用 /* 开头,以 */ 结尾。其中的全部内容均视为注释。
接下来让我们来挑战一下难度高一些的吧~
首先,我们双击 CAnimal 切换到它的头文件,然后我们在刚刚定义的变量下面再定义一个类成员函数 ( 方法 ) 。如 void OnMsg();
然后我们双击 CAnimal() 切换到源文件视图,在最下面的空白处输入这个类成员函数 ( 方法 ) 的实现代码。 如 void CAnimal::OnMsg() {}
然后在里面写上实现代码 AfxMessageBox(Cat); (因为是在本类的作用域范围内,类成员之间可以互相直接访问,所以可以访问 Cat 的值)
完成了之后,我们到需要调用的地方加上调用的代码~
编译后执行,点击 “确定” 按钮,我们就可以看到弹出的消息框啦~
哈哈,完成了难度较高的挑战是不是很高兴呢~
顺带说一下 MessageBox() 和 AfxMessageBox() 的区别,MessageBox() 只可以在 CDialog 的子类中使用 AfxMessageBox() 不受这个限制。
好啦,今天就到这里啦~ 我们明天继续探索Visual C++ 的奥秘吧~