结构体类型可以很方便地根据程序设计需要将某些具有内在联系的相同或不同类型的数据组合成一个有机的整体,便于统一使用。
但结构体也存在着一些不可避免的局限性:数据和代码是分享的,即结构体定义只包含数据成员,将所有对数据的处理放在函数中进行。这样在大型应用软件的编写和后期维护升级过程中,程序员不仅要考虑代码,还必须时时刻刻考虑数据结构,一量数据结构需要变更,就必须修改与之相关的所有模块的程序代码,使得代码可重用性差,维护代码代价高。一旦程序达到一定规模,程序的复杂性超过了结构化程序设计所能管理的限度,它就会变得难以处理和控制。
将数据和处理数据的函数当成一个整体,就能很好地解决上述的问题,这就是面向对象程序设计的编程思想。
结构体只是把数据封装在一起。突破这个概念,将处理数据了函数和数据一起“封装”成一种新的编程类型,这就是面向对象程序设计的类了。封装可以形象地理解为以类的格式为大包,将各种不同类型的相关数据封在包内同时将其处理函数也打包在一起。
函数的“声明、定义和调用”三部曲延伸到类,类的三部曲包括:
1 类的声明:类也是一种自定义的构造数据类型,它是将不同类型的数据和与这些数据相关的操作函数封装在一起的集体体,类中的数据和操作函数分别称为数据成员和成员函数。声明一个类,也就是声明类的数据成员和成员函数。
2 类的成员函数定义:类的成员函数定义可以放在类声明的外部来实现。成员函数的定义形式为:
类名::成员函数名()
{成员函数体}
3 类的应用:C++把类的“变量”改称为“对象”。因此“对象”与一个基本类型“变量”的地位和作用是一样的,在类的基础上定义的“对象”才是应用类来编程要处理的对象,其定义形式也相同,格式如下:
类名:对象名列表;
对象是类的实例,在类定义后可根据需要定义多个实例-对象。之后类的应用就像结构成员的引用一样来引用对象的成员,只是现在的成员有两部分:数据成员和成员函数。一般来说,只有在类中被定义为公有的数据成员或成员函数才能够在类外被引用,引用符“.”,其一般形式是:
对象名.公有数据成员;
对象名.公有成员函数(实参列表);
类将数据成员和成员函数封装在一起,成员函数直接处理数据成员,可以保证私有数据成员的安全性。所有类既有“牢固的屏障”保护着私有成员的安全,又有公有成员函数作为良好的接口与外界交流。
#include <iostream.h> const int N = 5; class Array_max // 类声明(可含有成员函数实现) { public: void set_value(); void max_value(); void show_value() { cout<<"max="<<max<<endl; } private: int array[N]; int max; }; void Array_max::set_value() // 成员函数实现 { int i; cout<<"请输入"<<N<<"个整数:"<<endl; for(i=0;i<N;i++) cin>>array[i]; } void Array_max::max_value() // 成员函数实现 { int i; max=array[0]; for(i=1;i<N;i++) { if(array[i]>max) max = array[i]; } } void main { Array_max dx; dx.set_value(); // 成员函数调用 dx.max_value(); dx.show_value(); }
面向过程程序设计采用的是结构化程序设计方法。其核心是功能分解。当程序员试图编程解决一个实际问题时,第一步要做的是将问题分解成若干模块,接着根据模块来设计数据的表示方式,然后编写函数对这些数据进行处理,最终的程序是由这些函数构成的。在整个设计过程中,模块的数据处于功能实现的从属地位,着重点在编写函数,但程序员在编程时又必须时时考虑到数据,因为函数就是针对具体数据的操作。
这种数据和处理数据的函数的分离,给编程人员带来沉重负担。一旦后期数据结构需要变更,就必须修改与之有关的所有模块,因此代码可重用性差,维护代价高。一旦程序达到一定的规模,程序的复杂性超过了结构化程序设计技术所能管理的限度,它就会变得难以处理和控制。数据和函数的分离,是面向过程的程序设计在大型软件开发时的瓶颈问题,也是面向对象的程序设计着力解决的问题。
面向对象的程序设计,顾名思意就是以对象-“数据”为主导的一种程序设计。面向对象的程序设计思想出现得很早,只是人们认识它是从C++语言开始的。C+语言是以数据为主导,以算法为辅助的面向对象的程序设计语言。在面向对象的程序设计中,数据和处理这些数据的操作函数构成了一个整体-类,这样数据结构发生变化时,只要对相关类进行修改,并不需要做大量的修改工作,即可实现对软件系统的修改,降低了软件维护成本。
在日常生活中,对象就是人们认识世界的基本单元,它可以是人,也可以是物体或者一件事。整个世界都是由形形色色的“对象”组成的,例如一个学生、一块黑板、一场比赛等。对象可以很简单,也可以很复杂,复杂的对象可由若干简单的对象构成。那么,对一组具有共同属性特征(数据)和行为特征(函数)的对象的抽象就是“类”。例如,由许多学生的共同特征可以抽象出“学生类”,而具体某一个学生就是“学生类”的一个实例-对象。
可见,类和对象之间是抽象和具体的关系。以面向对象的观点来看,一个对象是由描述其属性的数据和处理这些数据的行为-函数组成的实体,是由数据和函数共同构成的。类是对一组对象的抽象,这组对象具有相同的数据属性和行为特征(操作函数),在对象所属的类中要说明这些数据和操作函数。进行C++程序设计时,有了类,才能创建对象,一个对象是类的一个实例。就如同C语言中由系统定义了整形数据int的相关特征,用户才能够定义int型变量,并用该变量编程一样,C++的编程需要用户自己先定义“类”这种新的类型,才能定义该类的对象,并用该类对象编程。
面向对象程序设计中最重要的概念是类和对象,最显著的特征是三大特性-封装性、继承性和多态性。
1 封装性:封装与数据抽象密切相关,它们在现实世界中广泛存在。以手机为例,手机上有若干按键,当人们使用手机时,只需根据自己的需要按下相应的按键,手机就会完成相应的工作。这些按键装在手机表面,人们通过它们与手机交互,而手机内部电路是封装在机壳里的,其内部原理对用户来说是隐藏的,这就是“封装”。
那么如何知道手机上哪个键是拨打、哪个键是挂断呢?这是手机的使用说明书告诉我们的。但使用说明书一般不会告诉我们内部电路是如何工作的。也就是说,说明书在手机做什么和怎么做之间做了明确的分工。
将这些观点应用于类,就不难理解数据的抽象和封装。将数据和处理数据的操作函数组成一个实体,数据的具体结构和对数的操作细节隐藏起来,用户通过操作接口对数据进行操作。对于用户来说,只需要知道如何通过操作接口对该数据进行操作,而不需要知道具体是如何做到的,也不需要知道数据如何表示,这就是“封装”。
对象的这一封装机制,可将对象的使用者和设计者分开,使用者像手机用户一样,不必知道对象行为实现的细节,只需按照设计得提供的接口去做。封装的结果实际上隐藏了复杂性,并提供了代码重用性,从而减轻了开发和维护一个软件系统的难度。同时,封装可以防止程序员对对象内部的数据成员和成员函数进行不必要的干涉,提高了系统的安全性。
2 继承性:继承在现实生活中是一个很容易理解的概念。例如,每人都继承了父母的某些特征。从面向对象程序设计的观点来看,继承所表达的是类之间延续的关系。这种关系使得某类可以继承另一个类的属性特征和行为特征。例如,动物是一个类,狗是动物中的一种,具有动物的一般特征,因此可以继承动物类的某些特性,产生出一个新的狗类,而无须重新定义一个新类。
因此,在面向对象程序设计中,继承的作用有两个:一是避免公用代码的重复开发,减少代码和数据冗余;二是通过增强一致性来减少模块间的接口和界面。继承机制为程序员提供了一种组织、构造和重用类的手段。
3 多态性:多态性的意义在于,类的各个对象能以不同的方式响应同一消息,即所谓的“同一接口,多种响应方式”,或者说属于不同类的函数可以共用一个函数名,或者同样的信息,会因接收的个体的不同而有不同的动作,例如同样是“出行”,飞机出行、火车出行、汽车出行、......,会因为交通工具的不同,实现的方式截然不同。
单一继承的一般格式:
class <派生类名>:<继承方式><基类名>
{派生类成员声明;};
多重继承的一般形式: