C++面向对象程序设计(第三版)
上QQ阅读APP看书,第一时间看更新

1.1.3 面向对象程序设计的基本特征

面向对象程序设计方法模拟人类习惯的解题方法,代表了计算机程序设计的新的思维方法。这种方法的提出是对软件开发方法的一场革命,是目前解决软件开发面临困难的最有希望、最有前途的方法之一。本节介绍面向对象程序设计的4个基本特征。

1.抽象

抽象(abstraction)是人类认识问题的最基本的手段之一。抽象是将有关事物的共性归纳、集中的过程。在抽象的过程中,通常会忽略与当前主题目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象是对复杂世界的简单表示,抽象并不打算了解全部信息,而只强调感兴趣的信息,忽略了与主题无关的信息。例如,在设计学生成绩管理系统的过程中,只关心他的姓名、学号、成绩等,而对他的身高、体重等信息就可以忽略。而在学生健康信息管理系统中,身高、体重等信息必须抽象出来,而成绩则可以忽略。

抽象是通过特定的实例(对象)抽取共同性质后形成概念的过程。面向对象程序设计中的抽象包括两个方面:数据抽象和代码抽象(或称行为抽象)。前者描述某类对象的属性或状态,也就是此类对象区别于彼类对象的特征物理量;后者描述了某类对象的共同行为特征或具有的共同功能。正如前面所述的,对于一组具有相同属性和行为的对象,可以抽象成一种类型,在C++中,这种类型为类(class),类是对象的抽象,而对象是类的实例。

抽象在系统分析、系统设计以及程序设计的发展中一直起着重要的作用。在面向对象程序设计方法中,对一个具体问题的抽象分析结果是通过类来描述和实现的。

现在以职工人事管理系统为例,通过对所有职工进行归纳、分析,抽取出其中的共性,可以得到如下的抽象描述:

(1)共同的属性:姓名、职工号、部门等,它们组成了职工类的数据抽象部分。用C++的数据成员来表示,可以是:

(2)共同的行为:数据录入、数据修改和数据输出等,这构成了职工类的代码抽象(行为抽象)部分。用C++的成员函数表示,可以是:

如果开发一个学生成绩管理系统,所关心的特征就有所不同了。可见,即使对同一个研究对象,由于所研究问题的侧重点不同,也可能产生不同的抽象结果。

2.封装

封装(encapsulation)是面向对象程序设计方法的一个重要特性。在现实世界中,所谓封装就是把某个事物包围起来,使外界不知道该事物的具体内容。在面向对象程序设计中,封装是指把数据和实现操作的代码集中起来放在对象内部,并尽可能隐蔽对象的内部细节。对象好像是一个不透明的黑盒子,表示对象属性的数据和实现各个操作的代码都被封装在黑盒子里,从外面是看不见的,更不能从外面直接访问或修改这些数据及代码。使用一个对象时,只需知道它向外界提供的接口形式而无须知道它的数据结构细节和实现操作的算法。

C++对象中的函数名就是对象的对外接口,外界可以通过函数名来调用这些函数来实现某些行为(功能)。这些将在以后进行详细介绍。

所谓封装具有两方面的含义:一是将有关的数据和操作代码封装在一个对象中,各个对象相对独立、互不干扰;二是将对象中某些数据与操作代码对外隐蔽,即隐蔽其内部细节,只留下少量接口,以便与外界联系,接收外界的消息。这种对外界隐蔽的做法称为信息隐蔽。信息隐蔽有利于数据安全,可以防止无关人员访问和修改数据。

封装的好处是可以将对象的使用者与设计者分开,大大降低了人们操作对象的复杂程度。使用者不必知道对象行为实现的细节,只需要使用设计者提供的接口即可自如地操作对象。封装的结果实际上隐藏了复杂性,并提供了代码重用性,从而减轻了开发一个软件系统的难度。

3.继承

继承(inheritance)是面向对象程序设计的重要特性。继承在现实生活中是一个很容易理解的概念。例如,我们每一个人都从父母身上继承了一些特性,如种族、血型、眼睛的颜色等,我们身上的特性来自父母,也可以说,父母是我们所具有的属性和行为的基础。

下面以哺乳动物、狗、柯利狗之间的关系来描述“继承”这个特性。图1-2说明了哺乳动物、狗、柯利狗之间的继承关系。哺乳动物是一种热血、有毛发、用奶哺育幼仔的动物;狗是有犬牙、食肉、特定的骨骼结构、群居的哺乳动物;柯利狗是尖鼻子、具有红白相间的颜色、适合放牧的狗。在继承链中,每个类继承了它前一个类的所有特性。例如,狗具有哺乳动物的所有特性,同时还具有区别于其他哺乳动物如猫、大象等的特征。图中从下到上的继承关系是:柯利狗是狗,狗是哺乳动物。“柯利狗”类继承了“狗”类的特性,“狗”类继承了“哺乳动物”类的特性。

图1-2 哺乳动物、狗、柯利狗之间的继承关系

从面向对象程序设计的角度出发,继承所表达的是对象类之间相关的关系。这种关系使得某一类可以继承另外一个类的特征和能力。

若类之间具有继承关系,则它们之间具有下列几个特性:

(1)类间具有共享特征(包括数据和操作代码的共享)。

(2)类间具有差别或新增部分(包括非共享的数据和操作代码)。

(3)类间具有层次结构。

假设有两个类A和B,若类B继承类A,则类B包含了类A的特征(包括数据和操作),同时也可以加入自己所特有的新特性。这时,称被继承类A为基类或父类;而称继承类B为A的派生类或子类。同时,还可以说,类B是从类A中派生出来的。

如果类B是类A的派生类,那么在构造类B时,不必描述类B的所有特征,只需让它继承类A的特征,然后描述与基类A不同的那些特性。也就是说,类B的特征由继承来的和新添加的两部分特征构成。

如果类B是从类A派生出来,而类C又是从类B派生出来的,那么就构成了类的层次。这样,又有了直接基类和间接基类的概念。类A是类B的直接基类,是类C的间接基类。类C不但继承它的直接基类的所有特性,还继承它的所有间接基类的特征。

具体地说,继承机制允许派生类继承基类的数据和操作(即数据成员和成员函数),也就是说,允许派生类使用基类的数据和操作。同时,派生类还可以增加新的操作和数据。

如果没有继承机制,每次的软件开发都要从“一无所有”开始,类的开发者们在构造类时,各自为政,使类与类之间没有什么联系,分别是一个个独立的实体。继承使程序不再是毫无关系的类的堆砌,而是具有良好的结构。

采用继承的方法可以很方便地利用一个已有的类建立一个新的类,这就可以重用已有软件中的一部分甚至大部分,在派生类中只需描述其基类中没有的数据和操作。这样,就避免了公用代码的重复开发,增加了程序的可重用性,减少了代码和数据的冗余,大大节省了编程的工作量,这就是常说的“软件重用”思想。同时,在描述派生类时,程序员还可以覆盖基类的一些操作,或修改和重定义基类中的操作。具体的实现方法将在以后进行详细介绍。

从继承源来分,继承分为单继承和多继承。

单继承是指每个派生类只直接继承了一个基类的特征。前面介绍的动物链,就是一个单继承的实例。

单继承并不能解决继承中的所有问题。例如,小孩喜欢的玩具车既继承了车的一些特性,又继承了玩具的一些特征。玩具车与玩具、车之间就形成了多继承的关系。

多继承是指多个基类派生出一个派生类的继承关系。多继承的派生类直接继承了多于一个基类的特征。

4.多态

多态(polymorphism)也是面向对象程序设计的重要特性。在现实世界中,多态性经常出现。假设一辆汽车停在了属于别人的车位,司机可能会听到这样的要求:“请把你的车挪开。”司机在听到请求后,所做的工作应该是把车开走。在家里,一把凳子挡住了孩子的去路,他可能会请求妈妈:“请把凳子挪开。”妈妈过去搬起凳子,放在一边。在这两件事情中,司机和妈妈的工作都是“挪开”一样东西,但是他们在听到请求以后的行为是截然不同的,这就是现实世界中的多态性。对于“挪开”这个请求,还可以有更多的行为与之对应。“挪开”从字面上看是相同的,但由于作用的对象不同,操作的方法就不同。

面向对象程序设计借鉴了现实世界的多态性。面向对象系统的多态性是指不同的对象收到相同的消息时产生多种不同的行为方式。例如,有一个窗口(Window)类对象,还有一个汽车(Car)类对象,对它们发出“移动”的消息,“移动”操作在Window类对象和Car类对象上可以有不同的行为。

C++支持两种多态性,即编译时的多态性和运行时的多态性。编译时的多态性是通过重载来实现的,运行时的多态性是通过虚函数来实现的。

重载一般包括函数重载和运算符重载。函数重载是指一个标识符可同时用于为多个函数命名,而运算符重载是指一个运算符可同时用于多种运算。也就是说,相同名字的函数或运算符在不同的场合可以表现出不同的行为。下面给出一个函数重载的例子。

在此,重载了3个函数,名字都是Print()。它们有各自不同的功能,分别用“语句段1”“语句段2”“语句段3”中的语句实现,在此略去了语句的细节。这3个函数的函数名相同,但函数实现的操作不同。那么,当收到要求使用Print()函数的消息时,到底应该执行哪一个函数呢?这就要看消息传递时函数实参的类型是什么,根据实参的类型来调用不同的同名函数。例如,发送的消息是Print(20),则执行的是“语句段1”;发送的消息是Print(12.34),则执行的是“语句段2”;发送的消息是Print("welcome"),则执行的是“语句段3”。

使用重载可以使程序员在只知道操作的一般含义、而不知道操作的具体细节的情况下能够正确地调用某个函数,减少了程序员记忆操作名字的负担。

如果不能使用重载,我们必须为不同函数确定不同的函数名,如PrintInteger()和Printdouble()等。程序员将需要记忆很多不同的函数名,增加了程序员的负担。

由于虚函数的概念略为复杂,并且涉及C++的语法细节,将在第6章再进一步讨论。

多态性增强了软件的灵活性和重用性,为软件的开发与维护提供了极大的便利。尤其是采用了虚函数和动态连编机制后,允许用户以更为明确、易懂的方式去建立通用的软件。