分享
 
 
 

C++实例教学-类的应用

王朝c/c++·作者佚名  2008-06-01
窄屏简体版  字體: |||超大  

类<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进行是否为空的测试。

9.2 职员档案治理程序

是这一个简化了的雇员档案治理程序。其中把雇员的档案数据和对这些数据的设置、修改、删除等操作组成一个程序模块。程序通过这个模块----类的公有部分对档案数据进行处理,实现了面向对象程序设计的“封装”功能。

// program 6_2.cpp

#include<iostream.h>

#include<string.h>

class employee{

char *name; // 雇员姓名

short age; // 年龄

float salary; // 工资

public:

employee();

void set_name(char *);

void set_age(short a){age=a;}

void set_salary(float s){salary=s;}

void print();

~employee(){delete[]name;}

};

employee::employee()

{

name=0;

age=0;

salary=0.0;

}

void employee::set_name(char *n)

{

name=new char[strlen(n)+1];

strcpy(name,n);

}

void employee::print()

{

cout<<"Name: "<<name;

cout<<"Age: "<<age;

cout<<"Salary: "<<salary<<endl;

}

void main()

{

char *na=0;

short ag=0;

float sa=0;

na=new char[10];

employee emp[5];

for(int i=0;i<5;i++)

{

cin>>na;

cin>>ag;

cin>>sa;

emp[i].set_name(na);

emp[i].set_age(ag);

emp[i].set_salary(sa);

emp[i].print();

}

emp[3].set_salary(5100.0);

emp[3].print();

emp[1].~employee();

cin>>na;

emp[1].set_name(na);

emp[1].set_age(23);

emp[1].set_salary(2000.0);

emp[1].print();

}

说明:

这是一个简化的雇员档案治理程序,其中5~33行定义了一个employee类,它包含了一个雇员的档案数据,及对这些数据的若干处理函数。

构造函数employee,它在进行结象说明时被自动调用。这时创建的是一个雇员的空档案。

第39行说明的是一个对象数组,一次建立了五个雇员的空档案。

set_name(),set_age(),set_salary()三个函数可用来为雇员档案填 入姓名、年龄、工资。其中填 入姓名时要创建一个长度为该姓名字符串长度+1的字符数组,以便以字符串形式存放该雇员的姓名。

print()函数的功能是输出该雇员的档案内容。

析构函数~employee()的功能是当某雇员档案撤消或改成另一姓名时,把原占用的字符串空间释放掉。

--------------------------------------------------------------------

9.3 在平面上用直线和圆画图

编一个程序,以直线段、矩形和圆为基本图元组成一图形。

我们采用面向对象的编程方法,把直线line,矩形rectangle和圆circle设计为三个类,它们各有自己的数据和函数成员。用线、矩形和圆组合成图形,就是对这三个类的对象进行操作。

程序代码如下:

// program 6_3.cpp

#include<iostream.h>

#include<process.h>

#include<graph.h>

#include<conio.h>

class Line{

int startx,starty,endx,endy;

public:

Line(int sx,int sy,int ex,int ey)

{

startx=sx;

starty=sy;

endx=ex;

endy=ey;

}

void draw();

};

class Rectangle

{

int uplx,uply,lowrx,lowry;

public:

Rectangle(int ulx,int uly,int lrx,int lry)

{

uplx=ulx;

uply=uly;

lowrx=lrx;

lowry=lry;

}

void draw();

};

class Circle

{

int centx,int centy,radius;

public:

Circle(int cx,int cy,int r)

{

centx=cx;

centy=cy;

radius=r;

}

void draw();

};

void Line::draw()

{

_moveto(startx,starty);

_lineto(endx,endy);

} // 画一条直线

void Rectangle::draw()

{

_rectangle(_GBORDER,uolx,uply,lowrx,lowry);

} // 画一个空心矩形

void Circle::draw()

{

_ellipse(_GBORDER,centx-radius,centy-radius,centx+radius,centy+radius);

} // 画一个圆

void main()

{

short centx,centy;

cin>>centx>>centy;

Rectangle fram1(centx-60,centy-60,centx+60,centy+60),

fram2(centx-62,centy-62,centx+62,centy+62);

Circle disk(centx,centy,55);

Line poin1(centx,centy,centx+25,centy+25),

poin2(centx,centy,centx+12,centy+40),

bar1(centx,centy+50,centx,centy+42),

bar2(centx,centy-42,centx,centy-50),

bar3(centx-50,centy,centx-42,centy),

bar4(centx+42,centy,centx+50,centy);

if(!_setvideomode(_MAXRESMODE))

exit(1);

fam1.draw();

fram2.draw();

disk.draw();

bar1.draw();

bar2.draw();

bar3.draw();

bar4.draw();

poin1.draw();

poin2.draw();

getch();

_setvideomode(_DEFAULTMODE);

}

程序说明:

1) 这是一个利用基本图形对象直线段、矩形和圆来组成一个简单的表形的程序。从这个程序的设计过程(虽然它比实际程序简化了许多)可以了解面向对象程序设计的基本方式和特征。在55~79行的main函数中首先根据需要说明若干个图元对象:两个矩形,一个圆,六个直线段,它们正是组成显示的图形的各基本图元。最后分别调用各自的draw函数,完成了显示任务。

2) 程序中采用了在图形模式下进行屏幕显示的操作方式。系统的屏幕显示可有两种基本模式:文本模式:这是系统平时工作的模式。 图形模式:当程序需要输出图形时,应把系统设为图形模式。

用setvideomode()函数把系统由文本模式转为图形模式。

在各个类的draw()函数中,利用图形库函数完成基本图元的生成显示。

在6~16和41~44行,定义了一个Line类,它由四个私有成员和两个公有成员的函数成员组成,这完全是为了简化程序。例如,还可以为该直线段设置颜色。查阅坐标。修改数据等等操作。

在18~28行和46~48行,定义了Rectangle类。

在30~39行和50~51行,定义了Circle类。

三种类的定义,只是说明了类。在58~66行的对象说明中才有了类的具体内容,同时按照 给出的不同参数,决定了这些实际的图元的位置和尺寸。对于Line,Rectangle,Circle这三个类,用户可以对其进行的操作只有两种:

1) 通过构造函数 创建具有指定参数的对象;

2) 调用公有成员函数,显示这一图元。

从这里可以感受到类和对象对于其数据封装的含义。

从这个例子中,我们指出了把程序以类的形式组织起来的方法才优越性,但也应从中发现一些问题和不足的地方。例如:

三个类是互不相关的,但仔细观察会发现类Line和类Rectangle之间本来是有内在联系的,一个矩形实际上是由两条横线和两条纵线组成的,类Rectangle的对象的创建是否可以借助于类Line的几个对象的创建来实现呢?

另外三个类中都包含了象draw()这样的公有函数,也许可以具有象着色color()等等这样的共同的函数成员。

如何在程序设计中表现不同的类之间的层次关篛,相互间的相关性,这要用到其它的一些知识,如派生类、虚函数等概念和手段,使程序的组织更加合理和严密。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有