VC入门 面向对象程序设计的概念_第1页
VC入门 面向对象程序设计的概念_第2页
VC入门 面向对象程序设计的概念_第3页
VC入门 面向对象程序设计的概念_第4页
VC入门 面向对象程序设计的概念_第5页
已阅读5页,还剩25页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第2章 面向对象程序设计的概念精讲VC应用程序是采用C语言编写的。C+是在C语言的基础上进行改进与扩充,是既面向对象又面向过程的一种混合程序设计语言。传统的程序设计思维方式是基于“算法+数据结构=程序”的模式,在这类程序中,数据和施加于数据的操作(算法过程)总是分离的,所有程序均有一组被动的数据和一组能动的过程所组成。通常把这类程序设计称为面向过程的程序设计,把支持这类程序设计的语言称为面向过程的语言(procedure-oriented language ,简称POL)。C语言就是其中的一种。C保留了C语言的基本语法和流程,C程序几乎都可以在C环境中不作修改被运行。面向对象程序设计(obje

2、ct-oriented programming,简称OOP)将数据及对数据的操作放在一起,作为一相互依存、不可分割的整体来处理,它采用数据抽象和信息隐藏技术。它将对象及对象的操作抽象成一种新的数据类型类,并且考虑不同对象之间的联系和对象类的重用性。概括为“对象+消息=面向对象的程序”。本章将在C语言基础上对比介绍C的基本语法,并扩充到面向对象程序设计的基本概念,使读者能够对在VC应用程序开发中遇到的语法现象有个初步的理解。2.1 C+与C的基本语法区别2.1.1 一般语法区别1 注释C+支持的注释方法有两种:(1) /*/(2) /“/*/”是C语言中所使用的注释方法,在C中继续延用,一般在连

3、续多行注释时使用。“/”只能单行注释,进行注释的内容从“/”后的第一个字符直到本行结束。在使用AppWizard生成的MFC应用框架中会默认自动插入大量注释如程序清单2-1所示,“/ TODO”注释行提醒程序员添加代码的位置和通常所作的操作。程序清单2-1: 加注释的C+程序BOOL CMainFrame:PreCreateWindow(CREATESTRUCT& cs)if( !CFrameWnd:PreCreateWindow(cs) )return FALSE;/ TODO: Modify the Window class or styles here by modifying/

4、 the CREATESTRUCT csreturn TRUE; 注释一行不要太长,一般60个字符以内(保证在VC+集成编辑环境的可见区域之内),如有超过,建议换行处理。2 常量的定义在C语言中,符号常量的定义是通过宏定义#define来实现的,其实质是字符串替换。例如下面语句定义了一个符号常量PI:#define PI 3.1415926 在C+中保留#define定义符号常量的方法,但更多地主张采用关键字const来定义符号常量。例如PI的定义可以用下面语句表示:const float PI=3.1415926;注意:用关键字const定义的符号常量必须指定其数据类型,而用宏定义的符号常量

5、是没有数据类型的;用关键字const定义的符号常量在编译时为其分配相应的内存空间。规定了符号常量的数据类型,便于编译系统检查参数的数据类型,C的编译系统检查比C更为严格。3 变量的定义在c语言的函数体中,定义部分和执行语句部分是相对分离的,对局部变量的定义必须出现在函数体或分程序内所有执行语句之前。C+的函数体不再区分定义部分和执行部分,用户可在函数体的任意位置定义一个局部变量。因此一个变量尤其是中间变量,在需要时再定义,这样便于阅读与维护。例如可以在for语句中定义循环变量:int c=1;for(int i=0;i<10 ;i+) c*=2; 在C+程序中,要注意区分各种变量的作用范

6、围和生命周期,变量的类型有:(1) 局部变量(local variable)在一个函数体内定义的变量,只在本函数范围内有效,在本函数外不能使用。(2) 形参变量(formal parameter)向函数传送的变量,同时又能象其他局部变量一样使用。(3) 全局变量(global variable)在函数体外定义的变量,其有效范围为:从变量定义处开始直到本源文件结束。(4) 成员变量(member variable):在一个类中定义的变量,对类中的所有成员函数都是有效的。在一个继承体系中,根据访问类型和继承类型的不同,对派生类的作用范围会有不同,参见后面章节对继承的讨论。4 强制类型转换C+保留了

7、C语言的显式强制类型转换法:(类型名)表达式同时还提供了函数表示法:类型名(表达式)例如:int i=int(1.35); /给i赋一个初始值函数表示法的函数名只能使用简单类型名称,即int、float、double、char。5 动态变量创建C语言中用函数malloc()动态分配存储空间,用函数dispose()动态释放已分配的存储空间。C+中提供了两个类似该函数的运算符new和delete,它们能完成malloc()和dispose()的功能,而且更为方便和优越。(1) new 运算符使用new 运算符向内存的“堆”申请某个大小的存储空间,返回存储空间的首地址。空间大小由数据类型决定,分配

8、失败返回空指针。使用new的语句格式如下:指针=new数据类型(初始化值)指针=new数据类型算术表达式 /分配拥有多个连续空间的动态数组例如:int *pi ,*parray;pi=new int(85); /分配一个int型的内存单元,赋初值为85,首地址赋给int型指针piparray=new int10; /分配10个int型(数组)的内存单元,首地址赋给int型指针parray(2) delete运算符delete运算符用来释放通过new运算符得到的内存空间。使用delete的语句格式如下:delete 指针 delete 指针 /释放整个数组占用的空间 例如:delete pi;

9、/释放p指向的内存单元delete parray; /释放parray指向的动态数组内存块,方括号内不要写元素个数6 输入输出语句在C+中继续支持使用C的外部库函数(如printf和scanf等)进行输入输出,以兼容C程序的执行。C+本身引入了流的概念来处理数据的输入输出。所谓的流就是指数据之间的传输操作,包括输入流和输出流。输入流表示数据从载体(如数据文件)或设备(如键盘)流向内存的数据结构,输出流表示数据从内存的数据结构流向载体或设备(如显示器)。流的操作由C+的标准库iostream.h提供,也就是说,如果要使用流操作必须包含库文件“iostream.h”。输入输出流操作包含两个流对象:

10、输出设备cout和输入设备cin;两个操作符:插入操作符“<<”和抽取操作符“>>”。进行输出时的语句格式为:cout<<输出内容<<输出内容.;例如:cout<<”a=”<<a<<endl; /endl表示换行,等价于“n”进行输入时的语句格式为:cin>>变量1>>变量2.;例如:cin>>x>>y;程序清单2-2演示了两种不同的输入输出方法的使用:程序清单2-2:C与C+的输入输出比较(1)#include “stdio.h” (2)#include “io

11、stream.h” void main() void main() int a,b,sum; int a,b,sum; printf(“input a b:”); cout<<“input a b:”;scanf(“%d%d” ,&a,&b); cin>>a>>b;sum=a+b; sum=a+b;printf(“sum is %dn”,sum); cout<<”sum is ”<<sum<<endl; cout和cin的优点是可以自动调整输出和输入格式,而不必象printf和scanf都要由使用者一个个指

12、定;另外cout和cin支持用户自定义的数据类型(printf和scanf不支持)。2.1.2指针和引用引用是C的一个特征,可以把它看作是一个常量指针,并自动被编译器逆向引用。指针是指对象(变量)的地址,而引用是指给对象的地址取一个别名。引用通常应用于函数参数表中作为参数实现双向传值,但也可以作为一个引用变量使用。1 引用变量引用变量的说明方式是:<类型> &引用名=变量名或对象名;例如:int a;int &b=a; /合法定义引用类型int &c; /错!非法定义引用类型b+;这里b是a合法的引用变量,b是a的别名,不再为b分配空间,a,b共用同一存储单

13、元,对b的操作与对a的操作效果相同,执行b+等同于执行a+。引用变量定义时必须初始化,对此可以这样理解,指针变量的指向可以改变,但作为常量指针的引用,它的指向是不能改变的,所以在创建一个引用时,它必须指向一个特定的合法的存储空间,并且在程序运行过程中不能改变。2 引用参数最常看到引用的场合是在函数的参数表中。C与C+函数参数传送是采用单向传值,形参得到实参的拷贝值。如果需要逆向传值时,即将修改过的参数值反向传递回函数调用处,必须采用传地址的方法,即使用指针变量,直接操作内存空间。而在C+中更好的方法是使用引用参数,即把形参定义为引用变量,编译器会自动地实现逆向传值,引用具有更清晰的语法。 程序

14、清单2-3:引用参数举例#include "iostream.h"void AutoInc1(int*x);void AutoInc2(int &y);void main()int a(10); AutoInc1(&a); cout<<"a="<<a<<endl; AutoInc2(a); cout<<"a="<<a<<endl;void AutoInc1(int*x) (*x)+;void AutoInc2(int &y)y+;运行结果:

15、a=11a=12在上面的示例中,定义两个函数AutoInc1和AutoInc2的作用都是增1操作。AutoInc1形参采用的是指针,调用时需传递地址,而AutoInc2形参采用的是引用。可以看到AutoInc2的实现代码更为简单,调用时隐藏性好,即调用代码不能确定是传值还是传地址。2.1.3函数的改进1 函数原型C+中的函数原型其实就是C语言中的函数声明的概念,用于告诉编译系统使用函数的信息,包括函数的返回类型、参数个数和参数类型。函数原型通常放在程序的开始部分或者包含在头文件中,确保编译器在该函数被调用以前就已经“认识”该函数,其语法形式为:返回类型 函数名(参数表);例如:char* su

16、bstring (char* str, int start ,int count);也可写成:char* substring(char*,int, int);C的类型检查的机制较为薄弱,在很多情况下函数声明都可以省略,但在类型检查更为严格的C+中,所有函数皆必须声明后才可使用,这便是C+所特别要求的函数原型。编译系统对下面情况会给出错误信息:(1) 实参和形参的类型不匹配,且实参向形参自动类型转换非法。(2) 实参与形参的个数不一致。(3) 函数没有按函数的类型返回数据。例如:在C中main函数前必须指明返回类型为void,否则会出现编译错误,main()函数的一般定义如下:void main

17、()2 函数重载函数重载是指同一个函数名可以对应着多个函数的实现。函数重载允许一个程序内声明多个名称相同的函数,这些函数通常执行相同的功能,但是带有不同类型、不同数目的参数或不同类型的返回值。例如,要写一个求两个整数之和与两个浮点型数之和的函数如下:int plus (int x, int y)return x+y; /整数求和函数double plus(double x, double y)return x+y; /浮点数求和函数以上实现的是两个不同参数类型的重载。这样在调用时可用同一个函数名plus调用,程序会自动完成正确的调用。如:int z=plus(2,3); /调用整数求和函数pl

18、usdouble z= plus(2.0,3.5); /调用浮点数求和函数plus编译系统解读函数原型,识别重载。为了使编译系统能确定采用哪一个函数的实现,要求重载函数的参数个数和参数类型必须有所不同,否则产生二义性错误。3 设置默认参数调用函数时,若所用的参数有一个经常用的值,在声明函数原型时,可以用该值作为函数参数的默认值。凡有默认值的参数,在函数调用时如果提供实参,形参则接受实参传递的数值,如果没有给出实参,形参则接受默认值。例如,下面程序代码中的延时函数,第四次调用 delay()时没有给出实参,就按默认值1000延时,其它调用都给出了实参值,就按给定的数值延时。 程序清单2-4:函数

19、参数默认值举例#include “iostream.h” void delay(int t) if(t=0) return; for(int i=0; i<t;i+); cout<<”i=”<<i<<endl; void main() void delay(int=1000); /声明默认值参数 delay(0); delay(50); delay (500); delay(); delay(5000);运行结果为: i=50 i=500 i=1000 i=5000注意:(1) 默认参数只能在函数原型中给出,不能在函数定义中给出(2) 由于实参和形参的

20、匹配顺序是从左到右进行的,当不完全给出默认参数时,默认参数应集中置于形参序列的右端。下面的语句是错误的:int func(int x=3,int y=5,int z); int func(int x=3;int z;int y=5); 4 内联函数函数为程序设计的模块化作出了重要贡献,但函数的调用也会带来降低程序运行效率的问题,原因在于调用函数时,系统必须先保护现场,即保存当前的运行状态和代码地址,而后传递参数给形参,执行被调用函数,执行完毕返回时,需要恢复现场,即要恢复调用函数前的运行状态,根据保存的代码地址继续执行下一条指令。可见调用函数增加了时间和空间方面的开销,影响了程序运行效率。特别

21、是当频繁调用一个小函数时,执行函数代码的时间可能很短,而大量的时间花费在调用过程中的保护现场和恢复现场的工作上。C允许把代码少的小函数设置成内联函数,编译器为内联函数创建代码,在每次碰到该函数的调用都用相应的一段代码替换,避免了保护现场和恢复现场的开销,提高了程序的执行速度。从用户的角度看,调用内联函数和调用一般函数没有任何区别。内联函数有两种定义方法:(1) 在类的外部定义时,把关键字inline加在函数定义之前(2) 把函数原型声明和函数定义合并,放在类定义的内部,则不需要关键字inline程序清单2-5中所示的程序,在说明函数原型时前面加inline关键字,声明max函数为内联函数。程序

22、清单2-5:内联函数举例#include "iostream.h"void main()int a(8),b(16),c;inline int max(int x,int y);c=max(a,b);cout<<"c="<<c<<endl;int max(int x,int y)return(x>y?x:y);内联函数与宏在概念上是不同的,它是真正的函数。编译系统会进行类型检查,执行时会进行参数传递,与函数不同之处是在适当的时候能够象宏一样展开,取消函数调用的开销,所以在C中应该只使用内联函数,而不再使用宏定义。

23、内联函数的实现通常是在结构和规模比较简单的函数,如果过于复杂的函数,由于多处调用多处复制,反而会引起代码膨胀,就得不偿失了,另外内联函数不支持递归调用。2.2 类和对象类是面向对象程序设计的核心,类描述了具有一组相同特性(数据成员)和相同行为(成员函数)的对象,是对一组对象的抽象,它的实质是一种新的数据类型。通过类的结构可以规范一组对象的行为,这些行为是通过操作接口来描述的。使用者只需关心接口的功能,而不需要知道它是如何实现的。所以一个写的好的C程序比C程序更简单,更容易理解,通常面向对象程序需要较少的手工代码,因为问题的解决大部分都可以用已存在的库代码。2.2.1 类的定义类的一般定义形式为

24、:class 类名 private:私有段数据及函数;protected:保护段数据及函数;public:公有段数据及函数;程序清单2-6给出了一个点(Point)类的定义。程序清单2-6:Point类的设计(一)class Point int x;int y; public:void set_x(int k)x=k; void set_y(int k)y=k; int get_x()return x; int get_y()return y; ;其中Point是类名标识符。在此类中,x和y是两个私有数据成员,分别表示点的横坐标和纵坐标。set_x, set_y, get_x, get_y是四

25、个公有成员函数的定义。set_x和set_y函数分别用于设置x与y,即点的坐标值;get_x和get_y均是一个无参函数,分别返回点的坐标值。1 成员的访问类型上述定义形式中,数据成员或成员函数前面冠以private或protected或public,分别表示私有的、保护的、公有的。缺省时在类中定义成员都是私有的,所以上例中x和y的访问类型是private。(1) private私有的,是指只有类中所提供的成员函数才可以直接使用这些私有数据或调用这些私有函数,任何类以外的函数企图去使用这些私有数据或调用这些私有函数皆将导致编译时的错误。(2) protected保护段的成员除了被本类中的成员函

26、数存取及调用外,还可以被派生类的成员函数访问。但同样不能被其它类的函数访问。(3) public公有段的成员是可以被类内和类外的任何函数访问。一般情况下数据成员都定义为私有的,进行封装,不允许直接使用。成员函数定义为公有的,用来提供类与外界的操作接口,从而规范对象的行为,保护数据成员不被随意修改。而保护段成员的定义主要是从派生类和继承性的角度来考虑的。2 成员的组成类的成员分成两大类:数据成员和成员函数。(1) 数据成员数据成员可以是简单类型的变量,也可以是用户自定义类型的变量,甚至可以是已定义类型的一个对象。但是不能是寄存器类型或者是外部类型,另外不能定义自身类的对象作为数据成员。(2) 成

27、员函数类的成员函数的实现可以放在类定义内,也可以放在类定义外。一般当函数体的内容较小时,成员函数的实现放在类内,C+编译系统将在类内实现的成员函数按内联函数来处理,可以提高执行速度,定义时不必加inline关键字。而当函数体的大小不适合设定为inline函数时,则应将成员函数的实现放在类外,但在类外实现的成员函数必须在类内提供函数原型。这样的类的定义分成了两个部分,即类的定义部分和成员函数的实现部分。在VC应用程序中对类的组织也在应分为两个文件:类的头文件(.h)存放类的定义部分,类的实现文件(*.cpp)存放成员函数的实现部分。当类的成员函数的定义是在类的外部实现时,在定义方法时必须把类名放

28、在方法名之前,中间用作用域运算符“:”隔开,用来指明其所属的类。例如程序清单2-7所示为修改上述Point类的定义:程序清单2-7:Point类的设计(二)class Point int x;int y; public:void set_x(int k); void set_y(int k); int get_x();int get_y();void Point:set_y(int k)y=k; void Point:set_x(int k)x=k; int Point:get_x()return x; int Point:get_y() return y; 从功能上来分,成员函数可分为构造函

29、数、析构函数和普通成员函数。除了析构函数不能重载之外,其它成员函数都能重载。2.2.2 对象的定义对象的实质就是变量,对象的定义也就类似变量的定义,通常我们说对象是一个类的实例(instance)化。遵循同样的规则,同样存在着普通对象、对象数组、对象指针和对象指针数组,以及它们的定义和引用。例如使用定义好的Point类定义一组对象:Point p1,*p, pa100,*parray100;p1是一个Point类的对象,p是一个指向Point类的对象指针,pa是一个可以存放100个point的对象数组。对象定义后就可以访问对象中的成员,访问方法类似于C语言中结构体成员的访问。不同之处是,类的成

30、员规定了访问类型,私有类型和保护类型的成员只能被类的成员函数访问。所以通过对象只能访问公有数据成员和公有成员函数,如Point类中只能访问设定为公有类型的四个成员函数。成员引用的格式如下:对象名. 公有数据成员名 对象名. 公有成员函数名(实参表)对象指针->公有数据成员名对象指针->公有成员函数名(实参表)成员引用示例如程序清单2-8所示。程序清单2-8:实现Point类应用的main函数#include “iostream.h”void main() Point p1,*p; int x,y; p1.set_x(100);p1.set_y(100);p=&p1;x=p-

31、>get_x();y=p->get_y(); cout<<”x=”<<x<<endl<<”y=”<<y<<endl;运行结果为: x=100 y=1002.2.3构造函数和析构函数1 构造函数和析构函数构造函数和析构函数是类的特殊的成员函数。展开VC应用程序框架中任意一个类,都可以看到两个特殊的函数,一个完全与类名同名,是类的构造函数,另一个在类名前多一个符号“”,是析构函数,如图2-1所示。一个类可以看到一个或多个构造函数,但只能有一个析构函数。析构函数构造函数图2-1类的构造函数和析构函数构造函数的作用是定

32、义对象时,分配内存空间,使用给定的值初始化对象。析构函数的作用恰恰相反,在释放对象之前作一些必要的清理工作,主要清理系统分配的对象内存。构造函数的特点是它的函数名和类名相同,既可以在类内定义,也可以在类外定义;它可以有任意类型的参数,可以重载,但不能有返回类型说明,即不返回任何值。析构函数的特点是它的函数名和类名相同;前面有“”符,它没有参数,也没有返回值,而且也不能重载。构造函数和析构函数的调用都不需要用户干涉,当创建一个对象时,系统自动调用该类的构造函数,当对象退出作用域时,系统自动调用该类的析构函数。若用户没有定义构造函数和析构函数,系统将自动产生缺省的构造函数和析构函数。缺省的构造函数

33、将成员变量简单的设置为0或空,缺省的析构函数完成内存释放工作。对于大多数类来说,缺省的析构函数就能满足要求。但如果类的构造函数使用了new分配的内存,就必须自定义一个析构函数,在析构函数中使用delete来释放new分配的内存。而对象定义时能否进行初始化完全由构造函数决定,要由用户重载构造函数,指定初始化形式。2 构造函数和对象初始化简单变量定义时,可以直接设置初值,如:int i=1;在定义类的成员变量时,不能在类体中对成员变量进行初始化,只能在对象定义时初始化成员变量的值。对象的初始化实质就是调用构造函数,要为成员变量设初值,就要为构造函数设定相应的形参,在定义对象时,以实参的形式传值给构

34、造函数,构造函数就能使用给定值对成员变量设初值,方法如下:(1) 定义对象时使用函数运算符给出初值,如:Point p(100,150);(2) 定义带参构造函数,如:Class Point int x,y; public:Point();Point(int a ,int b);注意:构造函数决定对象定义的形式,一般在类定义时要给出无参构造函数,以允许在定义对象时不进行初始化,否则,若只有带参构造函数就必须进行初始化。(3) 构造函数给成员变量赋值构造函数给成员变量赋值有两种方式,一种是直接在函数体中使用赋值语句,如:Point:Point(int a,int b) x=a; y=b;另一种方

35、式是在构造函数参数表与函数体之间,以“:”和“成员名(形参)”的方式给出,如:Point:Point(int a,int b):x(a),y(b)3 拷贝初始化构造函数有时在创建一个对象时,希望用一个已经存在的对象来初始化这个对象,这时就需要拷贝初始化构造函数去完成初始化工作。拷贝构造函数的作用是完成同类对象间相互拷贝。拷贝构造函数是通过一个同类型的引用参数来完成的,它的格式为:T(const T&)例如,一个Point类使用拷贝构造函数的定义和使用如下:程序清单2-9:使用拷贝构造函数#include "iostream.h"class Pointint x,y;

36、 public:Point();Point(int a ,int b);Point(const Point& pa);int get_x()return x;int get_y()return y; Point:Point(int a,int b):x(a),y(b)/实现成员变量赋值Point:Point(const Point& pa)/ 实现对象拷贝x=pa.x; y=pa.y; void main()Point p1(100,150); /调用带参构造函数初始化p1 Point p2(p1); /以p1的值初始化p2 cout<<"x="

37、<<p2.get_x()<<endl;cout<<"y="<<p2.get_y()<<endl; 运行结果:x=100y=150 拷贝构造函数的另一个应用场合是一个数据成员是另一类的对象,对其进行初始化时,需要进行对象拷贝,该数据成员所使用的类需要提供拷贝构造函数。例如:class T1. T1(const T1&);class T2T1 a; int x; T2 (T1 b ,int x );在类T2中有一个数据成员a是T1类的对象,T2的构造函数中,直接用一个参数值b对a进行初始化,这就要求T1类必须提

38、供拷贝构造函数,支持对象拷贝。2.2.4 this指针this 是一个指向调用该成员函数的对象的常量指针。成员函数可通过this指针直接使用同类的其它成员,而不需要在每个成员之前加“对象名”和成员运算符“”。如图2-2所示,在CExam1_1View类的成员函数OnDraw中输入this,这时this指针表示的是CExam1_1View类,当输入箭头时,就会弹出一个下拉列表,显示CExam1_1View类的成员供选用。图2-2this指针的使用实际上,this指针在C+中纯粹是一个概念,实际编程时并不需要使用this,因为它是隐含的。理解this指针的目的是理解this指针的作用,即在成员函数

39、中使用某一数据成员时并不需要指出该数据成员是属于哪一个对象的,每一个数据成员之前都隐含有“this->”。2.2.5程序实例【例2-1】定义一个正方形类,该类包括:(1) 保护类数据成员,表示正方形的边长(值的范围为1-30个*),(2) 四个成员函数,功能分别为:取边长;设置边长;画正方形;在构造该类对象时,初始化正方形的边长。编制main函数实现:(1) 创建一个边长为5的正方形;(2) 调用成员函数打印正方形的形状;(3) 由用户输入正方形边长,修改边长的值;(4) 重新打印正方形。按照题目要求正方形Square类包括保护类数据成员len,公有类成员函数:带参构造函数,取边长函数G

40、etLen(),设置边长函数SetLen(),画正方形函数DrawSquare()。实现代码如程序清单2-10所示。程序清单2-10:实现正方形类及应用#include "iostream.h"class Squareprotected:int len;public: Square() Square(int x)len=x; int SetLen(int x); int GetLen()return len; void DrawSquare();int Square:SetLen(int x) if(x<1 |x>30 ) /检查x的值在1-30之间 retur

41、n 0; len=x; /设置边长 return 1;void Square:DrawSquare() int i,j;for(i=0;i<len;i+)for(j=0;j<len;j+) cout<<'*' cout<<endl;void main() Square s(5); cout<<"draw the square,len="<<s.GetLen()<<endl; s.DrawSquare(); cout<<"input the new side leng

42、th:" int x; cin>>x; if (!s.SetLen(x) cout<<"error, length should be between 1 and 30! " elsecout<<"draw the square,len="<<s.GetLen()<<endl; s.DrawSquare();程序运行结果如下:程序运行时定义了一个Square类的对象s,并定义边长为5个*,然后调用s的成员函数GetLen获得s的边长并输出边长信息,调用s的成员函数DrawSquare画

43、出图形。允许用户输入新的正方形边长,例如3个*,重复开始的输出步骤输出边长信息和图形。 2.3 继承继承描述的是类与类之间的关系问题。把在已有类的基础上定义新类的过程称为继承。继承的本质是实现代码重用,因而,继承机制能缩短软件的开发周期,加快编程速度。2.3.1基类和派生类被继承的类成为基类(或父类),基于父类并增加新特性从而派生出的类称为派生类(或子类)。派生类继承了基类中的部分或全部方法,也可修改基类中的方法,增加基类中没有的方法。定义派生类的一般格式如下:class 派生类名:继承方式 基类名 /本派生类新定义的成员;其中,派生类名是新定义的类名;继承方式有public、private、

44、protected三种,默认为private方式。继承方式决定了派生类对基类的访问性质。派生类对基类的继承情况如表2-1所示。表2-1不同继承方式下派生类对基类的访问权限继承方式基类成员访问权限派生类访问权限public(公有继承)publicpublicprotectedprotectedprivate不可访问protected(保护继承)publicprotectedprotectedprotectedprivate不可访问private(私有继承)publicprivateprotectedprivateprivate不可访问(1) public继承方式基类中的私有成员派生类不可访问,不

45、可继承,基类中其他类型的成员可以被派生类继承,而且继承后的访问权限不变。(2) protected继承方式基类中的私有成员派生类不可访问,不可继承,基类中其他类型的成员可以被派生类继承,但是继承后的访问权限都变成保护的。(3) private继承方式基类中的私有成员派生类不可访问,不可继承,基类中其他类型的成员可以被派生类继承,但是继承后的访问权限都变成私有的,即不可再向下派生。public继承方式揭示了父类与子类在概念上的相关性,子类应是父类的特例。当描述相关的两个实体时,一般采用public继承方式。构造函数不能被继承,因此,派生类的构造函数必须调用基类的构造函数来初始化基类的子对象。所以

46、,在定义派生类的构造函数时,除了对自己的数据成员进行初始化外,还必须调用基类的构造函数,初始化基类的数据成员。析构函数也不能被继承。所以,执行派生类的析构函数时,基类的析构函数也被调用,执行顺序是先执行派生类的析构函数,再执行基类的析构函数。【例2-2】设计两个类,一个类描述点,另一个类描述圆,圆由圆心和半径构成,圆类由点类派生而来,其中圆心的特性描述由点类继承下来。要求圆类提供(1)求圆面积的成员函数(2)取圆心坐标的两个函数(3)支持初始化的带参构造函数。如程序清单2-11所示,Point类为Circle类的基类,Circle类继承了Point类的点的特性,用于描述圆心,还继承取得点的坐标

47、的两个成员函数get_x()和get_y()。Circle类在基类的基础上进行了扩展,增加了半径的特性,并增加了四个成员函数:Circle类的构造函数的实现调用Point类的构造函数;获取圆心坐标的两个函数get_centreX()和get_centreY(),函数的实现直接调用get_x()和get_y()实现;求圆的面积函数Area()。程序清单2-11:派生类的实现和应用#include "iostream.h"class Point /定义基类int m_x, m_y;public:Point(int x,int y) m_x=x; m_y=y; int get_x

48、() return m_x; int get_y() return m_y;class Circle : public Point /定义派生类,公有继承 double radius;public:Circle(int x,int y,double r) : Point(x,y)/派生类没有继承基类的构造函数,而是 radius=r; /通过访问基类的构造函数初始化基类的数据成员。double Area(void) return 3.14*radius*radius; /计算圆面积int get_centreX() return get_x(); /调用基类中的成员函数访问基类数据,获得int

49、 get_centreY() return get_y(); /圆心坐标;void main()int x,y;double r;cout<<"x=" cin>>x;cout<<"y=" cin>>y;cout<<"r=" cin>>r;Circle c(x,y,r);cout<<"the centre of the Circle is "<<c.get_centreX()<<" and "

50、;<<c.get_centreY()<<endl;cout<<"the Area of the Circle is "<<c.Area()<<endl;运行结果:x=100y=100r=50the centre of the Circle is 100 and 100the Area of the Circle is 7850程序运行时,由用户输入圆心的坐标x,y的值,及半径r的值,例如x为100,y为100,半径为5。定义一个Circle对象c,以用户的输入值初始化这个对象。接着调用Circle类的成员函数get

51、_centreX()和get_centreY()输出圆心位置,调用成员函数Area()输出圆面积。 2.3.2多继承C+允许派生类有两种方式继承基类:即单继承与多继承。单继承指派生类只从一个基类派生,上一节中的Circle类即为单继承派生类;多继承指派生类从多个基类派生,派生类具有多个基类的特性。多继承派生类的定义格式如下:class 派生类名:继承方式1 基类名1,继承方式n 基类名n /本派生类新定义的成员;【例2-3】如图2-3所示,一个图形是由一个圆和一个矩形构成,要求求解图形的面积。设计三个类:其中两个是基类,一个基类描述圆,一个基类描述矩形;第三个派生类是由一个圆和一个矩形构成的图

52、形类,如下图所示。圆类包含数据成员半径和求圆面积的成员函数,矩形类包含数据成员长和宽,求矩形面积的成员函数。派生的图形类提供输出图形面积的函数,及支持初始化的带参构造函数。圆类 矩形类 图形类图2-3 图形类的构成程序清单2-12:多继承示例#include "iostream.h"const double PI =3.14; class Circle /定义基类 double radius;public:Circle(double r) radius=r; double CircleArea(void) return PI*radius*radius; /求圆面积;cla

53、ss Rectangle /定义基类double length,width;public:Rectangle(double x,double y)length=x,width=y;double RecArea(void)return length*width; /求矩形面积;class Graph:public Circle,public Rectangle /定义多继承派生类public:Graph(double r,double x,double y):Circle(r),Rectangle(x,y)void ShowArea(void) /求图形面积,调用基类成员函数double Tot

54、alArea; TotalArea= CircleArea()+RecArea();cout<<"the Area of Graph is "<<TotalArea<<endl;void main()double x,y,r;cout<<"r=" cin>>r;cout<<"x=" cin>>x;cout<<"y=" cin>>y;Graph g(r,x,y);g.ShowArea ();运行结果:r=10x

55、=20y=50the Area of Graph is 1314程序中定义了两个基类:Circle类表示圆类,包含一个数据成员半径radius,一个带参构造函数,和一个求圆面积的成员函数CircleArea();Rectangle类表示矩形类,包含两个数据成员长length和width,一个带参构造函数,和一个求矩形面积的成员函数RecArea()。程序中定义的派生类Graph 公有继承两个基类,派生类的带参构造函数访问基类的构造函数为基类的数据成员初始化,所以两个基类中必须提供带参构造函数。派生类的成员函数ShowArea调用基类的公有类成员函数CircleArea()和RecArea()获得圆面积和矩形面积,求出图形面积并输出。程序运行时,由用户输入圆的半径,矩形的长和宽,定义一个Graph对象g,由用户输入的数值初始化对象g。对象g调用成员函数ShowArea输出图形面积。2.4 虚函数2.4.1多态性前面我们介绍了一个类中的普通成员函数的重载,在一个继承的体系中,应该允许在派生类中对基

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论