第6章 面向对象程序设计基础.ppt_第1页
第6章 面向对象程序设计基础.ppt_第2页
第6章 面向对象程序设计基础.ppt_第3页
第6章 面向对象程序设计基础.ppt_第4页
第6章 面向对象程序设计基础.ppt_第5页
已阅读5页,还剩88页未读 继续免费阅读

下载本文档

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

文档简介

1、第6章 面向对象程序设计基础,6.1 面向对象程序设计概述 6.2 类和对象 6.3 继承和派生 6.4 多态性与虚函数 6.5 C+的I/O流 6.6 程序举例,Object Oriented Programming,6.1 面向对象程序设计(OOP)概述,面向对象程序设计是20世纪80年代发展起来的一种程序设计方法。它通过模拟现实世界中的事物和关系,利用抽象、分类、归纳等方法来构造软件系统,是一种更加自然直观的编程方式。 1. 面向对象程序设计的产生,在面向对象程序设计出现之前,人们一直采用结构化程序设计(Structured Programming,简称SP)来解决实际问题。,结构化程序

2、设计是面向过程的,其主要思想是采用自顶向下、逐步求精的方式对复杂问题逐步分解。按照结构化程序设计的要求,当需要解决一个复杂的问题时,首先应将它按功能划分为若干个小问题,每个小问题又可以按功能划分为若干个更小的问题,依次类推,直到最低一层的问题较容易用程序实现为止;然后将所有的小问题全部解决并把它们组合起来,复杂的问题就迎刃而解了。 缺点主要表现为: (1)数据和算法的一致性差。在结构化程序设计中,数据与处理数据的算法是相互分离的。当数据量增大时,程序会变得越来越难理解。 (2)程序的可重用性差。结构化程序设计并不支持可重用性,这就使得程序员在开发软件时每次都从零做起,重复着许多同样的工作。,针

3、对结构化程序设计在开发管理大型系统方面面临的困难,从20世纪70年代开始,程序设计人员便开始追求实现“数据抽象”的概念,经过不断地研究和改进,于1980年推出商品化的Smalltalk-80语言。这种程序设计语言引入了对象、类、方法等概念,引入了动态联编和继承机制,它标志了面向对象的编程语言已经建立了较为完整的概念和理论体系,也为解决大型软件管理、提高软件可靠性、可重用性、可扩充性和可维护性提供了有效的手段和途径。随后又逐渐推出了多种面向对象的程序设计语言,C+便是其中应用最为广泛的一种。 C+语言主要包括面向过程和面向对象两部分内容。 在C+面向过程的结构化程序设计框架中,函数是程序中完成一

4、定功能的模块,是程序的基本组成单元。各个函数之间通过参数、返回值和全局变量来进行数据通信。程序的主体通常由若干函数的定义构成。,使用结构化程序设计方法编写出的C+程序通常包括一个主函数和若干用户定义函数。主函数由操作系统调用,它是整个程序的入口。在主函数中调用其他函数,其他函数之间也可以相互调用,并且同一个函数可以被一个或多个函数调用任意多次。,函数调用关系示意图,一般地,一个C+程序的结构化程序设计框架可以表示为:, ,在C+的面向对象的程序设计框架中,类成为程序的基本组成单元。程序的主体通常由若干类的定义构成。 类可以把数据和函数封装在一起,用以描述事物的属性和对事物的操作。类与类之间一方

5、面通过封装而具有明确的独立性;另一方面又通过成员、友元、参数、继承和派生等关系,达到相互通信和联络的目的,从而形成一个有机的整体。 在结构化程序设计中,除了主函数之外,所有函数之间一律“平等”,没有层次关系可言,当程序规模稍大时,就会使整体结构变得相当混乱。相反,在面向对象程序设计中,类与类之间能够按照逻辑关系组成有条理的层次结构,从而使一个复杂程序变得有“纲”可循。这正体现出面向对象思想的优越性。,类之间层次关系示意图,一般地,一个C+程序的面向对象的程序设计框架可以表示为: ,对象:对象是指现实世界中无所不在的各式各样的实体。 对象由一组属性和一组行为构成。 属性是用来描述对象静态特征的数

6、据项,行为是用来描述对象动态特征的操作序列。 类:类是具有共同属性和行为的一组对象的集合。共同属性被描述为类中的数据成员,共同行为被描述为类中的成员函数。它为属于该类的全部对象提供了抽象的描述。 类与对象的关系犹如模具与铸件之间的关系,一个属于某类的对象称为该类的一个实例。 类具有封装性、隐藏性和继承性。 例如:一辆汽车可以用型号、颜色、载重量、行驶速度等信息进行描述,这些都是这辆汽车的属性;而开动汽车使它前进、后退、左转、右转等,都是对汽车状态的操作。这样,全部属性和操作的集合就定义了这种汽车的类型。,2. 基本概念,消息:向某对象请求服务的一种表达方式,是对象与外界、对象与其它对象之间联系

7、的工具。送到一个对象的所有可能的消息在对对象的类描述中都需要定义,即对每个可能的消息给出一个相应的方法。,发送消息,接收并响应消息,方法:对某对象接受消息后所采取的操作的描述。方法是在类定义中使用函数来描述的,使用一种类似于函数调用的机制把消息发送到一个对象上。,3. 特点,封装性:是指将数据和算法捆绑成一个整体,这个整体就是对象,描述对象的数据被封装在其内部。 例如:使用者不必知道一台电视机内部电路的具体构造和工作原理,就可以用它来收看电视节目。 这里有两个含义:第一个含义是把对象的全部属性和全部行为结合在一起,形成一个不可分割的独立单位; 第二个含义也称作“信息隐蔽”,即尽可能隐蔽对象的内

8、部细节,对外形成一道屏障,只保留有限的对外接口使之与外部发生联系。 C+中通过定义类和对象来实现对数据的封装,使得程序的修改维护更方便,封装性是OOP的基础。,继承性:指一种事物保留了另一种事物的全部特征,并且具有自身的独有特征。继承对于软件复用有着重要意义。 例如:建筑工程师已经设计出了一座普通楼房的图纸,后来又需要设计办公楼和居民楼。这时,可以有两种选择:一是从零开始,分别重新设计办公楼和居民楼;二是在普通楼房图纸的基础上分别添加新的功能,使它成为办公楼和居民楼。 利用现有类派生出新类的过程称为类继承,它支持代码重用、提供了无限重复利用程序资源的途径、节省程序开发的时间和资源,继承性是OO

9、P的关键。,多态性:多态性是指当多种事物继承自一种事物时,同一种操作在它们之间表现出的不同行为。例如:在一个使用面向对象思想编写的绘图程序中可能含有四种类型的对象,它们分别用于表示抽象概念形状和具体概念三角形、矩形、圆形。其中三角形、矩形、圆形对象都继承了形状对象的全部特征,并且三者都有一个名为“显示”的操作。但当用户对这三种不同的具体形状分别执行“显示”操作时,会在屏幕上得到三种不同的图案,这就是多态性。C+语言中使用函数重载、模板、虚函数等概念来支持多态性,多态性是OOP的重要补充。,结构体是将某一类事物的相关信息组合在一起构成的混合类型,它在某种意义上已经具有数据抽象的概念了,但其成员仍

10、由外部程序直接操作,而没有一个“接口”将其与外部程序隔开,这样,一旦结构本身发生改变,所有使用该结构的程序都要相应进行改动,因此不利于程序的维护。以此为基础的面向对象的程序设计则将数据(属性)和函数(行为)封装到称为类的软件包中,类的数据和函数紧密地联系在一起。 引例:,6.2 类和对象,#include iostream.h class Circle private: double x,y,r; public: void print() cout圆心:(x,y)endl; cout半径:rendl; void set(double x1,double y1,double r1) x=x1;

11、y=y1; r=r1; ;,类定义,数据成员,成员函数,Circle类将圆的属性(圆心坐标和半径)和操作(print、set)封装在一起,上述定义的Circle类实际上也相当于一种新的数据类型,包含了数据和对数据的操作,其成员描述如下:,void main() Circle p; p.set(0,0,2); p.print(); ,定义类对象,对对象调用 成员函数,同结构一样,类也是一种用户定义的数据类型,它是将不同类型的数据和与这些数据相关的运算封装在一起的集合体,类的结构是用来确定一类对象的行为,而这些行为是通过类的内部数据结构和相关的操作来确定的。,定义格式:,class 类名 publ

12、ic: ; private: ; protected: ; ; ,定义类的关键字,说明部分,实现部分,访问权限修饰符,6.2.1 类的定义,说明:,class B private: int year=2002, month=10, day=12; B b; ;,错,类的定义包括说明部分和实现部分。若成员函数在说明部分已定义,则实现部分可省略。 访问权限修饰符:public(公有)、private(私有 )和 protected(保护) 缺省时为private。 公有成员通常为成员函数,可在程序中引用,私有成员通常是数据成员,只有成员函数或友元函数才可引用。 类体中不允许对数据成员初始化。 自身

13、类的对象不可以作为自己的成员,如定义一个职工类,该类是对所有职工某些信息的抽象,包括如下成员:,class Staff private: char name30; char sex; float wage; public: void display(); void set(char *n,char s,float w); ;,void Staff:display() coutname:sex, wageendl; void Staff:set(char *n,char s,float w) strcpy(name,n); sex=s; wage=w; ,说明部分,实现部分,作用域运算符(类体外

14、实现时需要),类名仅提供一种类型定义,只有在定义属于类的变量后,系统才会为其预留空间,这种变量称为对象,它是类的实例。 1.定义形式 格式: 类名 对象名表; 假设A是一个已定义过的类,则可定义: A a1,*p,a5,指向A类对象的指针,A类的对象,对象数组,6.2.2 对象的定义,2. 成员引用,引用方式同结构成员,(1) 一般对象成员 数据成员:对象名.成员名 成员函数:对象名.成员名(参数表) (2) 通过指针引用对象成员 数据成员: 对象指针名-成员名 或 (*对象指针名).成员名 成员函数: 对象指针名-成员名(参数表) 或 (*对象指针名).成员名(参数表),#include i

15、ostream.h #include string.h class Staff private: char name30; char sex; float wage; public: void display() coutname:sex,; cout wageendl; void set(char *n,char s,float w) strcpy(name,n); sex=s; wage=w; ;,void main() Staff s,*s1; s.set(WangQiang,m,1526); s.display(); s1= ,s1为指向s的指针,可修改为如下形式吗? =W

16、angQiang; s.sex=m; s.wage=1526;,【例6-1】类和对象应用,1.构造函数和析构函数 【例6-2】看引例的另一种实现,#include iostream.h class Circle private: double x,y,r; public: void print() cout圆心:(x,y)endl; cout半径:rendl; Circle(double x1,double y1,double r1) x=x1; y=y1;r=r1; ;,void main() Circle p (0,0,2); p.print(); ,构造函数,同类名,无须额外的成员函数名

17、,6.2.3 对象的初始化,1.定义的同时初始化对象 2.省略对赋初值成员函数的额外调用,类的数据成员不能在声明时初始化,对象的声明呢?,构造函数特点:,若一个对象被定义在函数体内,则当该函数结束时,该对象的析构函数被自动调用。 当一个对象是使用new运算符被动态创建的,在使用delete释放时,析构函数将会被自动调用,是成员函数,可写在类体内,亦可写在类体外。 函数名同类名,不能指定函数类型,可以带参数。 可重载,即可定义多个参数个数不同的函数。 在创建对象时由系统自动调用,程序中不能直接调用。,是成员函数,可写在类体内,亦可写在类体外。 函数名同类名,前面多个字符“” ,不指定类型,无参。

18、 不可重载,即一个类只能定义一个析构函数。 可被调用,也可由系统调用。系统自动调用情况如下:,析构函数特点:,2. 缺省构造函数和缺省析构函数,形式: :( ) :( ) ,说明: 若没有定义任何构造函数,系统会自动生成上述无参的缺省构造函数及析构函数 若定义一个静态对象而没有指明初始化时,编译器会按缺省构造函数对对象的所有数据成员都初始化为或空。,【例6-3】定义一个Circle1类,具有求一个圆的面积、求两个圆的面积之和,以及输出面积等功能 。,#include iostream.h class Circle1 private: double x,y,r,s; public: void p

19、rint() cout圆心:(x,y)endl; cout半径:rendl; Circle1() Circle1(double x1,double y1,double r1) x=x1; y=y1; r=r1; void addarea(Circle1 p1,Circle1 p2) s=3.14*(p1.r*p1.r)+3.14*(p2.r*p2.r); void disp() cout面积:sendl; ;,void main() Circle1 p1(0,0,2),p2(1,1,4),p3; p1.print(); p2.print(); p3.addarea(p1,p2); p3.dis

20、p(); ,可缺省吗?验证一下,初始化了吗,#include #include class count int num; public: count(); count(); void process(); ; count:count() num=0;,count:count() cout字符个数:numendl; void count:process() while(cin.get()!=n) num+; coutendl; void main() count c; cout输入一个句子: ; cess(); ,【例6-4】析构函数示例,析构函数中输出处理结果(未设专门输出函数),析

21、构函数在程序结束 前由系统自动调用,3.拷贝初始化构造函数,拷贝初始化构造函数作为一种特殊的构造函数,用于将已知对象的全部数据成员拷贝给正在创建的同类对象。除了具有一般构造函数的特点外,还有如下特点: 只有一个参数,且是对某个对象的引用 如果类中没有说明拷贝初始化构造函数,则编译系统会自动生成一个缺省的拷贝初始化构造函数,形式为: 类名:拷贝初始化构造函数名(const 类名 public: void print() cout圆心:(x,y)endl; cout半径:rendl; Circle1(double x1,double y1,double r1) x=x1; y=y1; r=r1;

22、Circle1( Circle1 ,void main() Circle1 p1(0,0,2),p2(p1); p1.print(); p2.print(); ,拷贝初始化构造函数(引用做参数),已知对象做初值,【例6-5】拷贝初始化构造函数应用,普通构造函数,常对象是指用const来修饰的对象,将不能修改的对象声明为常对象,可以提高操作的安全性,如上一节缺省的拷贝初始化构造函数: 类名:拷贝初始化构造函数名(const 类名 y=j; private: int x,y; ; const A a(1,3); /a为常对象,不能被更新,等价: A const a(1,3);,2.常成员函数 形式

23、:类型说明符 函数名(参数表) const 说明: 关键字const位于函数声明的最后; 不修改对象数据成员的成员函数才能声明为const函数,构造函数和析构函数不能声明为const 只有常成员函数可以操作常对象,【例6-6】常成员函数示例,#include iostream.h class Time private: int hour,minute,second; public: Time() Time(int h,int m,int s) hour=h; minute=m; second=s; void settime(int h,int m,int s) hour=h; minute=m

24、; second=s; void print() const couthour:minute:secondendl; ;,不修改数据成员,定义为常函数,settime函数可声明为const吗?,void main() Time t1; const Time t2(10,23,34); /定义t2为常对象 t1.settime(11,15,20); t1.print(); /t2.settime(4,12,15); t2.print(); ,前面的解释符可去掉吗? 1.因为settime函数不是常成员函数; 2. C+规定只有常成员函数可以操作常对象;,3.常数据成员,对不应该被修改的数据成员声

25、明为const,可使其受到强制保护,以防被意外改动,但初始化方式与一般数据成员不同。,#include iostream.h class Decrement private: int num; const int dec; /将dec声明为常数据成员 public: Decrement(int n,int d):dec(d) num=n; void fdec() num=num-dec; void print()const coutnum=num,dec=decendl; ;,初始化列表的方式初始化,void main() Decrement A(50,10); coutbefore decr

26、ement:; A.print(); for(int I=1;I=3;I+) A.fdec(); coutafter decrementI:; A.print(); ,对非常量的数据成员也可以用这种初始化列表的方式进行初始化,方法是用逗号分隔不同的数据成员。例如: Decrement(int n,int d):dec(d),num(n),静态成员是为了解决同一个类的不同对象之间数据和函数共享的问题,它比全局变量在实现数据共享时更为安全,是实现同类多个对象数据共享的好方法。在类中,分为静态数据成员和静态成员函数。 1.静态数据成员 是类的成员,不是对象的成员,被所有对象所共享,在内存中只存贮一次

27、,它的值对每一个对象都是一样的。 定义或说明时前面加关键字static 初始化在类外进行,不加static和访问权限修饰符 初始化格式:数据类型 类名:静态数据成员=值 引用格式:类名:静态数据成员名,6.2.5 静态成员,【例6-7】静态数据成员示例程序,#include class Tc private: int i; static int k; public: Tc( ) i=0; i+; k+; void display() couti,k; ; int Tc:k=0;,void main() Tc A,B; A.display(); B.display(); ,创建A时k值0-1 创

28、建B时k值1-2,通过以下程序了解静态数据成员的声明、初始化的位置和限定及具有类对象共享的属性。,运行结果: 1,2 1,2,2. 静态成员函数,公有的静态数据成员可以直接访问,但私有的或保护的静态数据成员却必须通过公有的接口进行访问,一般将这个公有的接口称为静态成员函数。 静态成员函数在声明时,也要在类型说明符前加关键字static,静态成员函数是类的成员函数,而非对象的成员。 调用形式: 类名:静态成员函数名(参数表) 对静态成员函数中数据成员的引用也有区别: 对静态数据成员,直接引用 对非静态数据成员,通过对象引用 【例6-8】静态成员函数应用程序示例,#include class Tc

29、 private: int A; static int B; public: Tc(int a) A=a; B+=a; static void display(Tc C) coutC.AB; ; int Tc:B=2;,void main() Tc A(2),B(4); Tc:display(A); Tc:display(B); ,静态成员 函数的调用,非静态数据 成员的引用,静态数据 成员的引用,通过该程序了解程序中静态成员函数的调用方式以及静态成员函数中静态数据成员和非静态数据成员的引用方式,6.2.6 友元函数,类具有数据封装和隐藏的特性,只有类的成员函数才可以访问,外部函数只能访问类的

30、公有成员,但在某些情况下,需要在类的外部访问类的私有成员。若通过公有成员函数进行访问,由于存在参数传递、类型与安全检查等,将影响程序的运行效率。友元可以在类的外部直接访问类的私有成员。 友元提供了不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。对于一个类,可以在声明时前面加friend关键字将一般函数、其他类的成员函数或者其他类声明为该类的友元,使得这个类中本来隐藏的信息可以被友员访问,这在某些情况下可以提高程序的执行效率,但却破坏了类的封装性和数据的隐藏性。,如果友元是一般成员函数或是类的成员函数,称为友元函数;如果友员是一个类,则称为友元类,友元类的所有成员函数都

31、是友元函数。本节主要介绍友元函数。 说明: 友元函数说明时前面加关键字friend标识,以区别其它的函数,但实现时不允许使用该关键字。 友元函数为非成员函数,在类体内说明,在类体外实现,实现时前面也不能加类名。 友元函数可以访问类中的私有成员。,【例6-9】友元函数示例程序,#include #include class Cpoint private: int X,Y; public: Cpoint(int x, int y) X=x; Y=y; void print(); friend double dist(Cpoint ,void Cpoint:print() coutXY ; doub

32、le dist(Cpoint ,6.3 继承和派生,6.3.1 基类和派生类 继承是面向对象程序设计的一个重要特征,是软件复用的一种形式。新类通过这一方式从现有的类中继承数据和函数,并且可以重新定义或增加新的数据和函数,从而产生新类所需的功能,它克服了传统程序设计方法对编写出来的程序无法重复使用而造成资源浪费的缺点。 称已存在的用来派生新类的类为基类,又称为父类,由已存在的类派生出的新类称为派生类,又称子类,一个派生类可从一个基类派生,也可从多个基类派生,从一个基类派生的继承称为单继承,从多个基类派生的继承称为多继承。 请看下图:,基类(父类),派生类(子类),多继承,单继承,class Cy

33、linder1:public Circle private: double h; public: void print() Circle:print(); cout圆柱高:hendl; void set(double x1,double y1, double r1,double h1) Circle:set(x1,y1,r1); h=h1; ;,通过调用Circle类成员函数输出Circle类成员,class Cylinder private: double x,y,r,h; public: void print() cout圆心:(x, y)endl; cout半径:r ,圆柱高:hendl

34、; void set(double x1,double y1, double r1,double h1) x=x1; y=y1; r=r1; h=h1; ;,对比如下两个圆柱体类的定义,全部数据成员重新定义,只定义一个新成员(Circle中没有),从Circle类中派生而来,1.派生类的定义格式,class 派生类名: 继承方式 基类名 派生类新定义成员;,若要在派生类Cylinder1中再增加一个成员函数volume,用以求圆柱体的体积,则该成员函数能否如下实现:,double Cylinder1:volume() return(3.14*r*r*h);,错!,r是Circle类的私有成员,

35、 不能被派生类访问,6.3.2 单继承,2.继承方式,public,private和protected 是三种常用的继承方式,继承方式的不同决定了派生类对基类成员的访问权限不同。 派生类的继承关系,【例6-10】分析下列程序中的访问权限,并回答所提的问题。,#include class A public: void f1(); A( ) i1=10;j1=11; protected: int j1; private: int i1; ; class B:public A public: void f2( ); B( ) i2=20;j2=21; protected: int j2; priva

36、te: int i2; ;,class C:public B public: void f3(); C( ) i3=30;j3=31; protected: int j3; private: int i3; ; void main() A a; B b; C c; ,f2()能否访问f1(),i1,j1? 能访问f1()、j1,不能访问i1,(2) 能否访问f(),i1,j1? 能访问f1(),不能访问i1和j1,(3) f3()能否访问f2()、i2和j2,以及f1(),j1和i1? f3能访问f2()和j2,以及f1()和j1,但不能访问i1和i2,(4) c能否访问f2(),i2和j2?

37、以及f1(),j1和i1? 能访问f2()和f1(),其它的都不能访问,#include iostream.h class Circle private: double x,y,r; public: void print() cout圆心:(x,y); coutendl半径:rendl; void set(double x1,double y1,double r1) x=x1; y=y1; r=r1; double getr() return r; ;,class Cylinder1:public Circle private: double h; public: void print() C

38、ircle:print(); cout圆柱高:hendl; void set(double x1,double y1, double r1,double h1) Circle:set(x1,y1,r1); h=h1; double volume() double R=getr(); return(3.14*R*R*h); ; void main() Cylinder1 p; p.set(0,0,2,3); p.print(); coutvolume=p.volume(); ,【例6-11】派生类示例程序,3.派生类的构造函数和析构函数,说明: 基类中有缺省的构造函数或没定义构造函数,则派生类构

39、造函数的定义中可省略对基类构造函数的调用,而隐式调用缺省构造函数。 基类构造函数中,只有有参的构造函数,则派生类构造函数中必须 调用基类构造函数,提供将参数传递给基类构造函数的途径 派生类构造函数的调用顺序为先基类,后派生类。 派生类析构函数的执行顺序为先派生类,后基类。,派生类的构造函数除了对自己的数据成员初始化外,还负责调用基类构造函数使基类的数据成员得以初始化,当对象被删除时,派生类的析构函数被执行,同时基类的析构函数也将被调用。 格式: 派生类名(派生类构造函数总参数表):基类构造函数(参数表1) ;,#includeiostream.h #include string.h class

40、 Staff private: char name30; char sex; float wage; public: void display() coutname:sex, ageendl; Staff(char *n,char s,float w) strcpy(name,n); sex=s; wage=w; ;,【例6-12】派生类构造函数示例程序,class Staff1:public Staff private: int wt; public: Staff1(char *n,char s,float w,int t):Staff(n,s,w) wt=t; double addwage

41、() return(wt/10*80); void display() Staff:display(); coutwt, addwage()endl; ; void main() Staff1s(WangQiang,m,1526,21); s.display(); ,多态性是指一组具有相同基本语义的方法能在同一接口下为不同的对象服务。 例如:任何一辆汽车都有方向盘、操作杆以及用于控制的离合器、制动装置和油门的三个踏板,掌握了这个“接口”的操作,学会驾驶一种汽车就会驾驶任何类型的汽车。再如录音机、录像机或VCD等都有一组类似的控制键: 用来播放、录音、暂停等操作,尽管这些设备的内部结构和实现原理

42、有很大差异,但都用这一“接口”来进行十分相似的操纵。多态性在软件中也早已存在,如在一般的高级语言中,“+”既可以完成两个整数的相加,也可完成两个实数的相加,甚至完成两个字符串的连接等。这也体现了同样的方法被不同类型的对象接收时导致完全不同的行为。,6.4 多态性与虚函数,运算符重载 虚函数 函数抽象类,在C+语言中,多态性可分为两类:编译时的多态性与运行时的多态性。 编译时的多态性是通过函数重载和模板体现的,称为静态绑定;运行时的多态性是通过虚函数体现的,称为动态绑定。例如,很多应用软件都有复制、粘贴功能,而且被复制、粘贴的数据可以是各种各样的文字、图形等,但无论针对何种类型的数据,所点击的菜

43、单命令是一样的。这就是运行时的多态性的体现。 由于前面已经介绍过函数重载,本节将重点介绍:,6.4.1 运算符重载 运算符重载就是赋予已有的运算符多重含义,通过重新定义运算符使它能够用于特定类的对象以完成特定的功能。例如运算符既可以作为插入运算符,又可作为移位运算符。 1.重载原则: 1)运算符重载实际上重载为一个函数,故遵循函数重载的原则; 2)运算符重载不改变运算符的优先级、结合性和其语法结构,即单目的只能重载为单目运算符,双目的只能重载为双目运算符。,【例6-13】重载“+”运算 ,用以实现两个字符串的连接,#include iostream.h #include string.h #i

44、nclude stdio.h class Str private: char *s; int len; public: Str() Str(char *s1) len=strlen(s1); s=new charlen; strcpy(s,s1); void print() coutsendl; Str operator+(Str s1) return(strcat(s,s1.s); ;,void main() char str1100,str2100; gets(str1); gets(str2); Str s1(str1),s2(str2),s3; s3=s1+s2; s3.print()

45、; ,2.重载形式:,重载为类的成员函数: 类名 operator 运算符(参数表) 程序中表达形式: c1 运算符 c2 编译程序解释形式: c1 operator 运算符(c2),重载为类的友元函数: friend类名operator运算符(参数表) 程序中表达形式: c1 运算符 c2 编译程序解释形式: operator 运算符(c1,c2),将例6.13中的加法运算符重载为友元函数的形式如下:,friend Str operator+(Str s1,Str s2) return(strcat(s1.s,s2.s);,#include iostream.h class Complex

46、private: doublem_image; /描述实部 doublem_real; /描述需部 public: Complex (double i=0,double j=0) m_real=i;m_image=j; void show() coutm_real+m_imageiendl; Complex operator +(Complex ,【例6-13-1】设计复数类Complex,具有复数设置、输出、两个复数的加法和乘法功能。其中加法和乘法通过重载“ + ”和“ * ”运算符来实现。,方法2: #include iostream.h class Complex private: do

47、uble m_image; /描述实部 double m_real; /描述需部 public: Complex (double i=0,double j=0) m_real=i;m_image=j; void show() coutm_real+m_imageiendl; friend Complex operator +(Complex ,this是一个无需定义而隐含于每个类的成员函数中; this指向正在被某个成员函数操作的对象,即每个对象都可以通过this指针访问自己的地址; 可以用*this来标识调用该成员函数的对象; 通常情况下,程序中并不显式地使用this指针;,【例6-14】自

48、增运算符重载(this 指针使用示例),6.4.2 this 指针,#include iostream.h class A private: int x; public: A(int x1) x=x1; void print() coutxendl; A operator+() x+; return(*this); ; void main() A a(5); (+a).print(); ,自增重载运算作用于A类对象a,虚函数可以是另一个类的友元函数,但不得是静态(static)的成员函数。虚函数反映了基类和派生类成员函数之间的一种特殊关系,是实现多态性的工具。 1. 虚函数说明方式 virtu

49、al 类说明符 函数名 (参数表) 2. 虚函数的调用方式 非多态调用:不借助指针或引用的直接调用,通过成员访问运算符. 进行,不具备多态性特征。 多态调用:借助指向基类的指针或引用的调用。 3. 实现动态联编的条件 基类中有说明的虚函数 调用虚函数操作的只能是对象指针或对象引用,否则仍为静态联编,【例6-15】虚函数示例程序,6.4.3 虚函数,#include iostream.h class Animal public: void character() cout动物特征:不同.n; virtual food() cout动物食物:不同.n; ; class Giraffe:public

50、 Animal public: void character() cout长颈鹿特征:长颈.n; virtual food() cout长颈鹿食物:树叶.n; ;,class Elephant:public Animal public: void character() coutcharacter(); p-food(); void main() Giraffe g; f(/实参为派生类对象的地址 ,观察运行结果?,void f(Animal /实参为派生类对象 ,void f(Animal p)/形参为基类对象 p.character(); p.food(); void main() Gir

51、affe g; f(g);/实参为派生类对象 Elephant e; f(e);/实参为派生类对象 ,若将程序的相应部分修改为如下两种形式,再观察运行结果?,从上述程序中可见: 只有当虚函数操作的是指向对象的指针或是对象的引用时,对该虚函数调用采取的才是动态联编。,虚函数使用说明: 派生类中的虚函数应与基类中的虚函数具有相同的名称、参数个数及参数类型。 可以只将基类中的成员函数显式地说明为虚函数,而派生类中的同名函数也隐含为虚函数。 构造函数不得声明为虚函数,也不要在构造函数中对某个虚函数进行多态调用(因派生类对象的自有部分尚未形成)。,1.纯虚函数 是一种没有函数体的特殊虚函数。当在基类中不

52、能对虚函数给出有意义的实现时,需将其说明为纯虚函数,而将它的实现留给派生类去做。 格式: virtual 类型 函数名 (参数表)=0;,2. 抽象类 拥有纯虚函数的类称为抽象类。作用是将有关的子类组织在一个继承层次结构中,由它来为它们提供一个公共的根。 只能用作其它类的基类,不能建立抽象类对象 可说明抽象类指针和引用,指向其派生类,进而实现多态性。 不能用作参数类型、函数返回类型或强制转换的类型。,6.4.4 抽象类,#include #include class base protected: int x,y; public: virtual void setx(int i,int j=0

53、) x=i; y=j; virtual void disp()=0; ; class square: public base public: void disp() coutx*xendl; ;,class cube: public base public: void disp() coutx*x*xendl; ; class chpow: public base public: void disp() coutpow(x,y); ;,void main() base *ptr; square B; cube C; chpow D; ptr= ,抽象类指针指向不同的派生类对象B、C、D,实现多

54、态性,【例6-16】抽象类示例程序,6.5 C+输入输出流,6.5.1基本知识 在C+语言中,数据的输入和输出(I/O)包括:标准输入设备键盘和标准输出设备显示器、外存磁盘上的文件和内存中指定的字符串存储空间进行输入输出这三个方面。对标准输入设备和标准输出设备的输入输出简称为标准I/O,对在外存磁盘上文件的输入输出简称为文件I/O,对内存中指定的字符串存储空间的输入输出简称为串I/O。本章主要介绍标准I/O和文件I/O。 C+语言中的输入输出操作是由它定义的一个庞大的I/O流类的类库所提供的,它将数据之间的传输操作称为流,且流动是有方向的。从而为实现更高级别的抽象提供了技术保障。也为程序与设备

55、、程序与文件无关,提高程序设计的灵活性和通用性奠定了基础。 C+语言I/O流类的类库中包括的类主要有:ios、istream、ostream、iostream、ifstream、ofstream、fstream等。其层次结构与继承关系如下图:,ios,C+中部分I/O类的层次与继承关系图,C+系统中的I/O类库,其所有的类被包括在iostream.h, fstream.h和strstrea.h这三个系统头文件中,各文件包含的类如下: iostream.h包含有:ios、iostream、istream、ostream等。 fstream.h包含有:fstream、ifstream、ofstre

56、am和iostream.h及 fstreambase 中的所有类。 strstrea.h包含有:strstream、istrstream、ostrstream和iostream.h 及 strstreambase中的所有类。,内存,I/O设备,“流”就是一个字节的序列从一处向另一处“流动”的过程。C+流是指信息从外部输入设备(如键盘、磁盘或网络连接)向计算机内部(即内存)输入和从内存向外部输出设备(如显示器、磁盘或网络连接)输出的过程。,在一个程序文件中当需要进行标准I/O操作时,则必须包含头文件iostream.h,当需要进行文件I/O操作时,则必须包含头文件fstream.h。 使用格式:

57、#include ,1. 标准输出流 cout cout是系统定义过的ostream类的一个对象,该类提供的多种输出功能都适用于cout,包括:,使用流插入操作符()输出各种数据(已在第一章介绍) 通过成员函数put输出字符 通过成员函数write输出字符串,6.5.2 标准I/O流,常用标准输入/输出对象,(1) 成员函数put的使用 格式: cout.put(char c),#include iostream.h void main() char ch=A; cout.put(ch)endl; cout.put(O).put(k)endl; ,【例6.17】,或 cout.put(const char c),作用:在屏幕上输出一个字符,返回值为ostream类对象的引用,故函数可以连续调用。,(2) 成员函数write的使用 格式: cout.write(const char *str,int n),#include iostream.h #include void main() char

温馨提示

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

评论

0/150

提交评论