版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第八章 类的设计 在面向对象程序设计语言中,类是最重要的概念,同时,类的设计也是面向对象程序设计的主要工作。一个程序的结构如何、程序的运行效率如何以及程序的可读性可维护性如何,都与类的设计有直接的关系。 在本章中,将详细介绍几个有实际意义的类的设计与实现过程,通过对这些类的设计,使读者在加深对类的设计的理解的基础上,进一步加深对与类有关的一些基本概念的理解和运用。 8.1 计数器类的设计 顾名思义,计数器类就是用于对计数值进行管理的类,此类的功能是能够对整数计数器进行管理。计数器类的功能 (1)在生成对象时,能够自动为计数值清零。(2)计数值加1功能。(3)计数值减1功能。(4)获取计数值的功
2、能。计数器类的定义与实现 #include #define uMax 30000 class cCounter unsigned int cnt; public: cCounter(void) cnt=0; void increment(void) if(cnt 0 ) cnt-; unsigned int value(void) return(cnt); ;void main(void) cCounter cnt1,cnt2; int i; for(i=0; i10; i+) cnt1.increment( ); cnt2.increment( ); for(i=0; i5; i+) cnt
3、2.decrement( ); cout “The value of cnt1 is: ” cnt1.value( ) n; cout “The value of cnt2 is: ” cnt2.value( ) n; 执行结果:The value of cnt1 is: 10The value of cnt2 is: 5说 明 (1)在上述cCounter类的定义中,由于计数值为非负的,所以把计数值cnt定义为unsigned int,同时,把计数值变量的访问权限指定为private,这就使得外界只能利用此类中的成员函数来访问计数值cnt,而不能直接访问计数器变量,这样,就起到了数据隐藏的作
4、用。 (2)由于在类的构造函数中将计数器的初值赋以零,这就保证了在main( )函数中定义cnt1和cnt2对象时,能够使其计数初值为零,而不是某一随机数,从而能够保证程序运行的正确性。用类来进行程序设计时的如下一些优点: (1)通过定义构造函数能够保证对对象中的变量进行初始化,从而能够消除由于没有初始化而在程序运行过程中所带来的一些问题,特别是当类的数据成员是指针时,这一初始化过程就显得尤为重要。 (2)由于只能通过类的成员函数来修改计数器的值,这样就使得对程序的维护变得容易。 在上面例子中,要想为计数值加1或减1,就需要利用类中定义的成员函数来完成。cnt1.increment( ); /
5、计数值加1cnt2.decrement( ); /计数值减1例如: 同时,要想取得计数器的值也需要调用成员函数来完成。 上述操作只能通过调用类的成员函数来完成,实际程序设计过程中显得比较麻烦,使用起来也不自然。x=cnt1.value( );例如: 如果能够利用下述方式来完成上述功能的话,则类对象之间的运算就如同普通变量运算一样简便: +cnt1; /相当于cnt1.increment( );-cnt2; /相当于cnt2.decrement( );x=cnt1; /相当于x=cnt1.value( ); 显然,要想实现上述功能,就必须要对+运算符和-运算符进行重载,同时还要在类中定义变换函数
6、。通过使用运算符重载和变换函数来重新设计cCounter类,并实现上述运算功能 #include #define uMax 30000class cCounter unsigned int cnt;public: cCounter(void) cnt=0; cCounter operator +( ) if(cnt 0) cnt-; return(*this); int operator !( ) return(cnt = 0); operator unsigned int( ) return(cnt); ; int main( void) cCounter cnt1,cnt2; int i;
7、 for(i=0; i10; i+) +cnt1; +cnt2; unsigned int k=cnt1+5; cout “k:” k n; while( cnt2 ) cout“cnt2:”unsigned(cnt2)n; -cnt2; if (!cnt1) cout “cnt1 is 0”; else cout ”等运算符只能被重载为类的成员函数。 由于cCounter类中被重载的运算符都是单目运算符,因此,它们都是利用无参数的成员函数来实现的。 有了上述运算符的重载定义之后,下面的运算就变得可能了: +cnt1;它表示将cnt1对象中的计数值cnt加1。 在上面cCounter类中定义的
8、+运算符和-运算符的重载函数的返回值类型为cCounter,如果其返回值类型为void的话,则下面的计算将是不可能的: 注 意:unsigned int i;cCounter x;i=+x+5;原 因:由于 此时,如果+x的返回值是void的话,就不能够利用变换函数来取出x对象中的计数值了。i=+x+5;i=unsigned int(+x)+5;可以看成 由于operator+( )和operator( )也同类的成员函数一样,因此也可以同调用一般的成员函数一样来调用运算符重载函数。cnt1.operator +( );例如: 对逻辑非运算符!的重载,主要是用于判断计数值是否为零,根据上述重载
9、定义可知,若cnt1对象中的计数值为零,则语句: if( !cnt1 ) 的条件为真。 从上述对运算符的重载定义及其使用过程不难看出,一旦对运算符进行重载之后,对类对象的操作就如同对简单变量的操作一样方便、简洁。 k=a+i; /相当于+i; k=ai; k=ai+; /相当于k=ai; i+;+和-的前置运算和后置运算 在cCounter类中,也应该对前置运算符和后置运算符都进行重载处理。一般来讲,对前置运算符和后置运算符的重载处理应该如下分别进行: class cOperator public: type operator +( ); /前置运算符+重载 type operator +(i
10、nt); /后置运算符+重载 type operator -( ); /前置运算符-重载 type operator -(int); /后置运算符-重载 ;注 意: 前置运算符的重载函数没有参数,后置运算符的重载函数有一个整型参数,但此整型参数只起到标识作用,而没有其它意义,换句话说,在重载函数中是不使用此参数的。 重新定义cCounter类,使其包含对+和-的前置运算符和后置运算符的重载处理。#include #define uMax 30000class cCounter unsigned int cnt;public: cCounter (void) cnt=0; cCounter op
11、erator +( ) if(cnt uMax) cnt+; return( *this); cCounter operator +(int dummy) cCounter x= *this; if(cnt 0) cnt-; return ( *this); cCounter operator -(int dummy) /注:返回值不可说明为引用& cCounter x= *this; if (cnt 0) cnt-; return (x); int operator !( ) return (cnt=0); operator unsigned int( ) return(cnt); ; in
12、t main( ) cCounter cnt1,cnt2; printf(“cnt1+=%un”,cnt1+); /后置运算符 printf(“+cnt2=%un”,+cnt2); /前置运算符 printf(“cnt1-=%un”,cnt1-); /后置运算符 printf(“-cnt2=%un”,-cnt2); /前置运算符 return (0);执行结果:cnt1+=0+cnt2=1cnt1-=1-cnt2=0说 明 (1)这里的参数dummy只是用于表示是后置运算符重载而已,除此之外没有其它意义。(2) 函数 cCounter operator -(int dummy)的定义如果改为如
13、下形式,将出现问题:cCounter& operator -(int dummy) cCounter x= *this; if (cnt 0) cnt-; return (x); 因x是局部变量,函数返回后将释放,返回其引用将提示警告错误cCounter(或counter&) operator -(int dummy) cCounter& x= *this; if (cnt 0) cnt-; return (x); 因x是*this的别名,返回值仍然是减1后的结果,即同前置-,故错误.(2)在main( )函数中:说 明 对于前置运算符和后置运算符系统将根据有无参数进行自动判断。 +cnt2;
14、 cnt2.operator +( )相当于 cnt1+; cnt1.operator +(0)相当于 (3)由于operator +( )和operator-( )也是cCounter类的成员函数,因此,下面的写法也是正确的,只是可读性差些而已:说 明cnt1.operator +(0); /调用后置运算符重载cnt2.operator +( ); /调用前置运算符重载8.2 字符串类的设计 字符串类就是用于对字符串进行管理的类,此类的功能是能对字符串进行处理。 字符串类的功能 (1)在生成对象时,能够自动为字符串清空。(2)允许为对象进行初始化操作。 (3)允许取出字符串中的任一个字符。(
15、4)允许对字符串进行比较操作。(5)提供获取字符串的功能。字符串类的定义与实现 #include #include class cString char *s;public: cString(void); cString(const char *); cString(const cString &); cString( ) delete s; cString &operator=(const char *); cString &operator=(const cString &); char &operator (int); operator char *( ); /变换函数 void pri
16、nt(void) const printf(“%s”,s); friend int operator=(const cString &x, const cString &y) return (strcmp(x.s,y.s)=0); friend int operator !=(const cString &x, const cString &y) return (strcmp(x.s,y.s) !=0); ; cString:cString(void) /缺省构造函数 s=new char1; *s=0; cString:cString(const char *x) /构造函数 s=new c
17、harstrlen(x)+1; strcpy(s, x); cString:cString(const cString &x ) /复制构造函数 s=new charstrlen(x.s)+1; strcpy(s, x.s); cString & cString:operator=(const char *x) /赋值运算符重载 delete s; s=new charstrlen(x)+1; strcpy(s, x); return( *this ); cString &cString:operator= (const cString &x) /赋值运算符重载 delete s; s=new
18、 charstrlen(x.s)+1; strcpy(s, x.s); return( *this );char & cString:operator (int i) /下标运算符重载 return(si); cString:operator char *(void) /变换函数 char *p=new charstrlen(s)+1; strcpy(p, s); return(p); void main(void) cString x; cString y(“abcd”); cString z=y; cString a; a=”abcdef”; a3=a0; /如果 运算符重载的返回值不是c
19、har&, a4=z; /而是char,则a3等不能出现在=号左边 x=y; printf(“a: ”); a.print( ); putchar(n); printf(“x: ”); x.print( ); putchar(n); printf(“y: ”); y.print( ); putchar(n); printf(“z: ”); z.print( ); putchar(n); printf(“x=y: %dn”, x=y); printf(“y=z: %dn”, y=z); printf(“a=x: %dn”, a=x); printf(“y!=z: %dn”, y!=z); pri
20、ntf(“x!=y: %dn”, x!=y); printf(“a!=x: %dn”, a!=x); char c100=“cc”; strcat(c, a); printf(“%sn”, c);执行结果: a: abcazf x: abcd y: abcd z: abcd x=y: 1 y=z: 1 a=x: 0 y!=z: 0 x!=y: 0 a!=x: 1 ccabcazf说 明 (1)这里需要注意的是main( )函数中的strcat(c, a)语句,这里的c是字符数组,a是cString类型的对象,由于在cString类中定义了变换函数,所以,系统通过自动使用变换函数,就能够使作为对
21、象的a像一般字符串一样来使用。说 明 (2)需要注意的是,如果下标运算符 的重载函数的返回值不是char&,而是char,则a3等不能出现在赋值语句的左边,而只能出现在赋值语句的右边,这是由于返回的只是一个值(常量),同函数的返回值一样,只有当函数的返回值是指针或引用时其调用才能出现的赋值语句的左边。这时,下面的赋值语句是错的: a3=a0; 说 明 (3)在利用指针来管理字符串时,一定要注意对内存的申请和释放工作。在此类中定义的析构函数就是用于这一目的的。说 明 (4)本例中给出了多个运算符重载函数的定义,包括 、=、!=和=等,正确地掌握这些运算符重载函数的定义,将有助于提高C+语言程序设
22、计能力和技巧。 8.3 链表类的设计 链表是一种非常有用的数据结构,很多复杂的程序设计都需要借助于链表来实现。链表类主要用于对链表进行管理,尽管很多程序设计语言都可以定义链表,但C+语言在定义和实现链表方面还是有其显著特点的。 链表类的主要功能 (1)向链表中插入元素,主要包括向链表头插入元素和向链表尾插入元素。 (2)从链表中删去元素,主要包括删去链表头的元素和删去链表尾的元素。 一个类是cLink,它主要用来管理链表中每个结点中的数据以及指向下一个结点的指针;实现链表功能的类的设计 另一个类是cList,它主要用来对链表进行管理并提供对链表的操作。 链表的实现 #include #incl
23、ude class cLink friend class cList; cLink *next; char item20; public: cLink(char *x=“” ) strcpy(item,x); cLink *get_next(void) return(next); void print(void) printf(“%sn”, item); ;class cList protected: cLink *first; cLink *last; public: cList(void) first=last=new cLink;/or new cLink( ); cList( ) Cl
24、ear( ); delete first; cList &Insert(const cLink &); /表头插入结点 cList &Append(const cLink &); /表尾插入结点 cList &Delete(void); /删去表头结点 cList &Remove(void); /删去表尾结点 cList &Clear(void); /删去所有结点 void Print(void); /显示所有结点 ;/向表头插入结点cList &cList:Insert(const cLink &x) cLink *ptr=first; first=new cLink; *first=x;
25、first-next=ptr; return(*this);/向表尾插入结点cList & cList:Append(const cLink &x) cLink *ptr=last; *ptr=x; last=new cLink; ptr-next=last; return(*this);/删除表头结点 cList &cList:Delete(void) if(first !=last) cLink *ptr=first-next; delete first; first=ptr; return(*this);/删除表尾结点cList &cList:Remove(void) if (first
26、=last) ; else if (first -next=last) Delete( ); else cLink *now=first; cLink *pre; while(now-next !=last) pre=now; now=now-next; pre -next=last; delete now; return(*this); /删去所有结点cList &cList:Clear(void) cLink *ptr=first; while (ptr !=last) cLink *next=ptr-next; delete ptr; ptr=next; first=last; retu
27、rn(*this); /显示所有结点/void cList:Print(void) puts(“= = = = list = = = =”); int no=1; cLink *ptr=first; while (ptr != last) printf(“%d:”, no+); ptr-print( ); putchar(n); ptr=ptr -next; int main(void) cList ls; ls.Append(cLink(“Wang”); ls.Insert(cLink(“Zhang”); ls.Append(cLink(“Gong”); ls.Append(cLink(“Y
28、ang”); ls.Print( ); return(0); 执行结果:= = = = list = = =1: Zhang2: Wang3: Gong4: Yang说 明 (1)由于cList类在进行各种链表操作时,需要使用cLink类中的数据成员和成员函数,这样,如果能够无条件地访问cLink类中的private成员的话将是很方便的,基于这一考虑,在本程序中把cList类作为cLink类的友元来使用,这样,cList类中的所有函数都能够直接访问cLink类中的所有成员。说 明 (2)为了处理上的方便,cList类中的last指针始终指向一个空结点,这一空结点是在定义此类的对象时由构造函数自
29、动生成的,有了这个空结点就使得对链表的操作比较方便了。8.4 堆栈类的设计 堆栈是一种常用的数据结构,在实际程序设计过程中经常会用到。在本节中给出的堆栈类设计,比“数据结构”课程中所讲解的内容稍微有点扩充,不仅提供push(入栈)和pop(出栈)操作,还提供一些其他操作,其目的主要是为了提高读者的程序设计能力。 设计一个堆栈类Stack,用于对整型数据进行管理,要求:(1)在定义对象时能够确定堆栈的长度。 (2)给出push( )和pop( )函数的定义。 (3)通过运算符重载,实现对堆栈是否为 空的判断。 (4)定义变换函数,用于返回堆栈的长度。 (5)通过函数的递归调用,来计算堆栈中 数据
30、为0的元素的个数。 (6)给出main( )函数的定义,在函数体中合 理地调用上述每一个函数。 #include iostream.hclass Stack int size; int *ptr; int *top;public: Stack(int n) /构造函数 ptr=top=new intn; size=n; int *getPtr( ) return ptr; void push(int x) /push函数 if(top-ptr ptr) return *-top; else return 0; int operator !( ) /!运算符重载 return (top-ptr!
31、=0); operator int( ) /变换函数 return size; int count(int *p) /递归调用函数 static int m=0; if(p top) if( *p+ =0) m+; count(p); return m; ; void main( ) Stack s(10); int len; int *p=s.getPtr( ); s.push(1); s.push(0); s.push(0); s.push(2); s.push(0); s.push(1); if(!s) cout Zero=s.count(p)endl; if(!s) cout Pop=
32、s.pop( ) endl; len=s; cout Len=len endl运行结果: Zero=3 Pop=1 Len=10 说明: (1)定义了变换函数之后,就可以像len=s;这样来调用该函数,从而简化了程序设计过程。 (2)由于top始终指向栈顶下一个入栈数据的空位置,因此需要注意+和-运算符在push和pop函数中的用法。 (3)由于堆栈遵循“先进后出”的原则,因此,需要注意push和pop函数的数据存取顺序。 说明: (4)在本例中,count被定义为递归调用函数,其中的局部变量m被定义为静态局部变量,如果去掉static关键字,则该函数不能正常工作。 (5)在定义对象时需要做的
33、工作,实际上就是在构造函数中应该定义的功能。8.5 数组类的设计 数组也是常用的数据结构,当需要处理大量的同类型数据时,利用数组是很方便的。通过学习数组类的设计,使读者能够进一步掌握有关运算符重载等知识。 设计一个类Matrix,用于对一整型88矩阵进行管理。要求:(1)在生成对象时,能够自动为矩阵中的每个元素清0。(2)通过运算符重载,实现对两个矩阵的减法和乘法运算。(3)通过运算符重载,实现矩阵中主对角线上的元素是否 全为0的判断。(4)实现按列为矩阵输入数据的成员函数。(5)实现求矩阵中主对角线上元素的最小值的成员函数。(6)给出main( )函数的定义,在函数体中合理地调用上述 每一个
34、函数。 #include iostream.h“#define MLEN 8class Matrix int MMLEN MLEN;public: Matrix( ); Matrix operator*(Matrix&); Matrix operator-(Matrix&); int operator!( ); void cInput( ); int Minv( ); void disp( ); ; Matrix:Matrix( ) /构造函数 int i, j; for(i=0;i MLEN;i+) for(j=0;j MLEN;j+) Mij=0; Matrix Matrix:operat
35、or*(Matrix& x) /*运算符重载 int i,j,k; Matrix a; for(i=0;i MLEN;i+) for(j=0;j MLEN;j+) for(k=0;k MLEN;k+) a.Mij+=Mik*x.Mkj; return a;Matrix Matrix:operator-(Matrix& x) /-运算符重载 int i,j; Matrix a; for(i=0;i MLEN;i+) for(j=0;j MLEN;j+) a.Mij=Mij-x.Mij; return a;void Matrix:cInput( ) /按列输入数据 int i,j; cout In
36、put MLEN MLEN data: n; for(i=0;i MLEN;i+) for(j=0;j Mji;int Matrix:Minv( ) /求主对角线上元素的最小值 int i,Mi; Mi=M00; for(i=1;iMii) Mi=Mii; return Mi;int Matrix:operator!( ) /!运算符重载 int i; for(i=0;i MLEN;i+) if(Mii) return 0; return 1; void Matrix:disp() int i,j; for(i=0;i MLEN;i+) for(j=0;j MLEN;j+) cout Mij
37、; cout n; void main() Matrix M, N, P; M.cInput( ); N.cInput( ); P=M - N; P.disp( ); cout Min= P.Minv( ) endl; P=M * N; P.disp( ); cout Min= P.Minv( ) endl; if(!P) cout All elements are zero.n; else cout Non-zero elements exist.n; 说明:(1)将数组长度定义为符号常量,有利于对不同长度的数组进行管理,从而能够提高程序的可读性和可扩充性。 (2)对真、假值的判断以及零、非零的判断等,一般都是对逻辑非运算符(!)进行重载。8.6 用于
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2026年具身智能从技术验证走向规模化商业应用元年
- 土工织物在港口与航道工程中的功能作用
- 2026年实验室化学品安全监督培训
- 2026年商场盗窃防范培训
- 婴儿听力视力筛查与护理
- 文化、体育公共设施建设项目节能评估报告书
- 护理体态礼仪塑造专业形象
- 某麻纺厂质量检验操作准则
- 麻纺厂生产安全管理制度实施
- 2026年企业员工急救培训
- 配电箱设备防护维护技术方案
- 2026年苏州工业职业技术学院单招综合素质考试题库附答案
- 2025版《煤矿安全规程》解读
- 2026年安徽水利水电职业技术学院单招职业适应性考试题库及答案1套
- 采集动脉血课件
- 2025年江西省公务员考试行测真题解析试卷(含答案)
- 剧毒从业证摸拟考试及答案解析
- 西藏高标准农田施工方案
- 隧道施工环境监测方案
- 化学微格教学讲解
- 开闭所操作规程与安全规范
评论
0/150
提交评论