类<class)的概念是面向对象程序设计的核心概念。把对数据和数据的操作和处理封装在一个程序模块中的方法,可以说是人们经过几十年程序设计 实践的经验总结。把程序以类的形式组织成若干模块,使其获得了最佳的结构特性。类概念的引入使程序设计发生了革命性的转变。从结构程序设计(SP)中以作为程序的基本模块转变为面向对象(OOP)程序设计中,以类作为程序的基本模块,这一变化使程序设计技术出现了质的飞跃。 类的概念抓住了程序的本质。程序的基本元素是数据。而函数是围绕数据进行处理和操作。抓住了数据这个“纲”,程序中关系复杂的各种函数就变得脉络清楚,可以随着相应的数据组合成类,类的使用使得: * 程序设计本身更有条理了; * 程序的可读性更好了; * 程序设计的过程真正象是机器部件的组装; * 由于程序的零部件化,使得程序的可重用性变成切实可行的事。 为了学会OOP方法,首先让我们看看C++程序中类及其对象是怎样工作的。
9.1 设计一个栈类 栈(stack)是程序设计过程中经常遇到朝气一种数据结构形式,它对于数据的存放和操作有下面这样的特点: 1) 它只有一个对数据进行存入和取出的端口; 2) 后进者先出,即最后被存入的数据将首先被取出。其形式很象一种存储硬币的小容器,每次只可以从顶端压入一个硬币,而取出也只可以从顶端进行,即后进先出。 这样的数据存储和管理形式在一些程序设计中很有用。例如,编译系统中(这是一类比较复杂的程序),对于函数调用的处理、对于表达式计算的处理,都利用了栈这样的数据结构。
下面是一个关于栈的程序: // program 6_1.h #include<iostream.h> const int maxsize=6; // enum boola{false,true}; /*注:如果在TC中调试,应加上这一句*/ class stack{ float data[maxsize]; int top; public: stack(void); ~stack(void); bool empty(void); void push(float a); float pop(void); }; stack::stack(void) { top=0; cout<<"stack initialized."<<endl; } stack::~stack(void) { cout<<"stack destoryed."<<endl; } bool stack::empty(void) { return top==0?true:false; } void stack::push(float a) { if(top==maxsize) { cout<<"Stack is full!"<<endl; return; } data[top]=a; top++; } float stack::pop(void) { if(top==0) { cout<<"Stack is underflow!"<<endl; return 0; } top--; return data[top]; } void main() { stack s1,s2; for(int i=1;i<=maxsize;i++) s1.push(2*i); cout<<endl; for(i=1;i<=maxsize;i++) cout<<s1.pop()<<" "; for(i=1;i<maxsize;i++) s1.push(2.5*i); for(i=1;i<=maxsize;i++) s2.push(s1.pop()); do cout<<s2.pop()<<" "; while(!(s2.empty())); } 程序运行结果如下:
说明: 1、 第一行定义了一种用户定义的枚举(enum)类型,该类型的全部值在定义式中的{...}内说明,每个值是一个标识符,每一个值与一个整数对应,如果不指定的话,就按顺序与0,1...相对应。 程序中定义的枚举类型 名为bool,只有两个值,即false和ture。即falsh=0,true=1。 C++语言中提供的枚举类型,实际上是int类型的一个子集。每个值除了作为一个整数之外,可以由用户给出一个标识符形式的名字。这样比直接用0,1形式的整数更直观,例如还可以定义这样的枚举类型 : enum answer{no,yes,maybe}; enum listorde{no-order,in-order,in-reverse}; 前者指明用0,1,2表示‘否',‘是'‘可能'中不同的回答,后者则用0,1,2表示一个数据序列的三种状态:“无序”、“正序”(从小到大)、“逆序”。在程序运行中,nono-order完全和整数0起相同的作用,yes-和in-order则与整数1等价。 在11,22行指出函数empty()返回类型为bool型,即是说明该函数应返回值false或true,当然返回整数0或1都是对的。在23行其返回语句为return top==0?true:false;其中top==0是一个关系表达式,如当前int型变量top的值为0,即top==0为真(true),返回true,如当前top的值不为0,则top==0为假(false),即返回值为false。 2、5~14行为类stack的说明,其中: class是一个关键字,指出所说明的是一个类,标识符stack是用户为这个类起的名字。用花括号{ }括起来的是类的成员。 类stack共有7个成员: 6~7行是stack的数据成员,这两个数据成员是stack的私有成员。 私有成员用关键字private:说明,通常这个关键字可以省略,缺省状态下默认为private:成员。私有成员在类外不可访问。 第8行的关键字public:指出在其后所列的成员为公有成员,类的公有成员可以在类外被引用。 9~13行是类stack的五个公有函数成员。 第9行是与类stack同名的函数成员的说明,其定义在类外的15~18行。 函数成员的定义可以在类外也可以在类里面。如把第9行改为: stack(void) { top = 0;} 那么在类外就不用再说明该函数,这样的在类里面说明的函数默认为内联函数(inline)。在类外说明的函数,其函数名前必须加上"stack::"以表示该函数是类stack的一个成员。 名为stack的函数成员是类stack的一个特殊的函数成员,称为类stack的构造函数,它有如下特征: 1) 和类同名; 2) 可有不只一个构造函数(参数表不同); 3) 无返回类型; 4) 主要完成对象的初始化工作; 5) 在说明该类的一个对象时,系统自动调用其构造函数; 6) 类说明中如未说明构造函数,系统设置一个最简单的构造函数。 第10行是另一个特殊的函数成员,称作析构函数,其特征为: 1) 名为“~”加上类名。 2) 无返回类型。 3) 可以定义一个或缺省。 4) 在该类的对象退出其说明区域时自动执行。 5) 主要完成该对象撤消前的善后工作。 第11行的函数成员,名为"empty",返回类型为bool型,其功能是测试一下栈是否为空。为无参过程。可以改写为: bool empty(void){return top==0;}; 采用类内定义方式,可以取代22~24行。 第12行说明第4个函数成员push,无返回值,一个float 参数,其函数定义在类外的第25~32行,功能是把一个新的浮点数压入堆栈中,如果发现栈已满则输出一个信息。 第13行的float pop(void)是最后一个函数成员,其功能为测试栈当前是否为空,如不空,把栈顶的数据取出。其定义在33~40行。
3、42~57行是主函数,或说是整个程序的入口和出口。在其中做了下面的一系列工作: 第44行说明了类stack的两个对象s1和s2,即创建了两个具体的栈(空栈)。 类的说明(5~40行)只是定义,说明了其对象才是真的分配了内存,并通过自动调用构造函数完成了初始化。 45!46行,对于栈s1,做了maxsize=6次压栈操作,先后+存栈数据为:2,4,6,8,10,12。 48~49行,又对栈(对象)s1做了6次退栈操作,并依次显示输出结果,由输出顺序: 12 10 8 6 4 2 可以看出栈的“后进先出”的特征。 50~51行对栈s1再做压栈处理,应注意,在执行语句50~51之前,s1,top的值应为0(为什么?)变量i的值应为maxsize+1=7,因此该while循环语句共循环6次,顺序压进栈s1的数据应为: 15,12.5,10,7,12.5,15 54!56行是把栈s2的数据逐步退栈取出并显示输出,其输出的顺序为: 2.5 5 7.5 10 12.5 15 从这个例子,可以了解栈的特征,同时也知道了如何对一个类的对象进行操作。 1) 首先应说明该类的对象(44行); 2) 只允许对该对象(类)的公有成员引用; 3) 引用时应以对象名限定成员名,如: s1.push(2*i); 表示对栈s1进行压栈操作; s2.empty(); 表示对栈s2进行是否为空的测试。
<  
1/2 1 2 下一页 尾页 |