C程序设计》电子教案第9章多态性和虚函数.ppt_第1页
C程序设计》电子教案第9章多态性和虚函数.ppt_第2页
C程序设计》电子教案第9章多态性和虚函数.ppt_第3页
C程序设计》电子教案第9章多态性和虚函数.ppt_第4页
C程序设计》电子教案第9章多态性和虚函数.ppt_第5页
已阅读5页,还剩68页未读 继续免费阅读

下载本文档

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

文档简介

第9章 多态性与虚函数,9.1 多态性 9.2 虚函数 9.3 纯虚函数和抽象类,9.1 多态性,9.1.1 普通成员函数重载 9.1.2 构造函数重载 9.1.3 派生类指针,返回首页,9.1.1 普通成员函数重载,1函数重载的方法 2函数重载的表示形式 (1)在一个类说明中重载。 (2)基类的成员函数在派生类中重载。 3函数重载的注意事项 4函数重载的二义性,1函数重载的方法,例9-1:给出以下程序的运行结果。 #include int square(int x) return x*x; double square(double y) return y*y; main() ,cout“The square of integer 7 is“square(7)endl; cout“ The square of double 7.5 is“square(7.5)endl; return 0; 此程序的运行结果为: The square of integer 7 is 49 The square of integer 7.5 is 56.25,例9-2:用重载函数实现求圆和矩形的周长。 #include const double PI=3.1415; double length(float r) return 2*PI*r; double length(float x,float y) return 2*(x+y); void main() float a,b,r; coutr;,coutab; cout“矩形周长:“length(a,b)endl; 运行结果为: 输入圆半径:7 圆周长:43.981 输入矩形长和宽:3 4 矩形周长:14,2函数重载的表示形式,例9-3:分析以下程序的执行结果。 #include class Sample int i; double d; public: void setdata(int n) i=n; void setdata(double x) d=x; void disp(), cout“i=“i“,d=“dendl; ; void main() Sample s; s.setdata(7); s.setdata(7.5); s.disp(); 此程序的运行结果为: i=7,d=7.5,有3种编译区分方法: 1)根据参数的特征加以区分。 2)使用“ : ”加以区分。 3)根据类对象加以区分。,3函数重载的注意事项,在C+语言中,编译程序选择相应的重载函数版本时函数返回值类型是不起作用的。不能仅靠函数的返回值来区别重载函数,必须从形式参数上区别开来。例如: void print(int a); void print(int a,int b); int print(float a); 这三个函数是重载函数,因为C+编译程序可以从形式参数上将它们区别开来。但: int f(int a); double f(int a);,4函数重载的二义性,函数重载的二义性(ambiguity)是指C+语言的编译程序无法在多个重载函数中选择正确的函数进行调用。这些二义性错误是致命的,因而编译程序将无法生成目标代码。函数重载的二义性主要源于C+语言的隐式类型转换与默认参数。,例9-4:隐式类型转换造成函数重载二义性示例。 #include float abs(float x) return (x0?x:-x); double abs(double x) return (x0?x:-x); int main() coutabs(1.78)endl; /调用abs(double) /coutabs(-7)endl; /错误,编译程序无法确定调用哪一个abs()函数 在重载函数中使用默认参数也可能造成二义性。,返回本节,9.1.2 构造函数重载,构造函数可以像普通函数一样被重载,而且也可能是C+语言应用函数重载最多的地方,因为设计一个类时总是希望创建对象的同时能以多种方式初始化对象的内部状态,而构造函数只能有一个名字,即该类的名字。当建设一个可复用类库时,重载构造函数可以更好地提高类界面的完整性。,例: class X public: X ( ) ; X( int ) ; X ( int, char ) ; X ( float, char ) ; . ; void f ( ) X a ; / 调用构造函数 X() X b ( 1 ) ; / 调用构造函数 X(int) X c ( 1, c ) ; / 调用构造函数 X(int, char) X d ( 2.3 , d ) ; / 调用构造函数 X(float, char) . ,例9-6:分析下面程序的执行结果。 #include class TDate public: TDate( ); TDate(int d); TDate(int m,int d); TDate(int y,int m,int d); protected: int year; int month; int day; ; TDate:TDate( ) ,year=1999; month=11; day=24; coutyear“/“month“/“dayendl; TDate:TDate(int d) year=1999; month=11; day=d; coutyear“/“month“/“dayendl; TDate:TDate(int m,int d) year=1999; month=m; day=d; coutyear“/“month”/”dayendl; TDate:TDate(int y, int m, int d) year=y; month=m; day=d;,coutyear“/“month“/“dayendl; void main( ) TDate aday; TDate bdate(10); TDate cdate(8,8); TDate ddate(1998,1,1); 输出结果为: 1999/11/24 1999/11/10 1999/8/8 1999/1/1,返回本节,9.1.3 派生类指针,指向基类和派生类的指针是相关的。 例如: A * p ; / 指向类型 A 的对象的指针 A A_obj ; / 类型 A 的对象 B B_obj ; / 类型 B 的对象 p = / p 指向类型 B 的对象,它是 A 的派生类,例9-8:分析以下程序的执行结果。 class A_class char name80; public: void put_name(char *s) strcpy(name,s); void show_name( ) coutname“n“; ; class B_class:public A_class char phone_num80; public: void put_phone(char *num) strcpy (phone_num,num); void show_phone( ) coutphone_num“n“; ;,main ( ) A_class *p; /对象指针 A_class A_obj; /对象 B_class * bp; B_class B_obj; p=,(B_class *)p)-show_phone( ); / 用基类指针访问公有派生类的特定成员,必须进行类型转换 此程序的运行结果为: Zhang San Li Si 0731_12345678 0731_12345678,例9-9:写出下面的程序的执行结果。 #include class Student public: Student(int xx) x=xx; virtual float calcTuition( ); protected: int x; ; float Student:calcTuition() return float(x*x);, class GraduateStudent:public Student public: GraduateStudent(int xx):Student(xx) float calcTuition( ); ; float GraduateStudent:calcTuition( ) return float(x*2); void main( ) Student s(20); GraduateStudent gs(20); couts.calcTuition()endl; /计算学生s的学费 coutgs.calcTuition()endl; /计算研究生gs的学费 输出结果为: 400 400,返回本节,9.2 虚函数,9.2.1 静态联编与动态联编 9.2.2 虚函数的概念 9.2.3 动态联编与虚函数 9.2.4 虚函数的限制 9.2.5 虚函数与重载函数的比较,返回首页,9.2.1 静态联编与动态联编,1静态联编 静态联编是指联编工作出现在编译连接阶段,这种联编又称早期联编,因为这种联编过程是在程序开始运行之前完成的。在编译时所进行的这种联编又称静态绑定。在编译时就解决了程序中的操作调用与执行该操作代码间的关系,确定这种关系又称为绑定,在编译时绑定又称静态束定。下面举一个静态联编的例子。,例9-11:静态联编示例程序。 #include class Point public: Point(double i, double j) x=i; y=j; double Area() const return 0.0; private: double x, y; ; class Rectangle:public Point public: Rectangle(double i, double j, double k, double l);,double Area() const return w*h; private: double w, h; ; Rectangle:Rectangle(double i, double j, double k, double l):Point(i, j) w=k; h=l; void fun(Point 该程序的运行结果为: 0,2动态联编 从对静态联编的上述分析中可以知道,编译程序在编译阶段并不能确切知道将要调用的函数,只有在程序执行时才能确定将要调用的函数,为此要确切知道该调用的函数,要求联编工作要在程序运行时进行,这种在程序运行时进行的联编工作被称为动态联编,或称动态绑定,又叫晚期联编。 动态联编实际上是进行动态识别。,返回本节,9.2.2 虚函数的概念,虚函数是在基类中冠以关键字 virtual 的成员函数。它是动态联编的基础。虚函数是成员函数,而且是非static的成员函数。它提供了一种接口界面,并且可以在一个或多个派生类中被重定义。 说明虚函数的方法如下: virtual () 其中,被关键字virtual说明的函数称为虚函数。,例9-12:分析以下程序的执行结果。 #include class Base public: void who( ) cout“basen“; ; class first_d:public Base public: void who( ) cout“First derivationn“; ;,class second_d:public Base public: void who( ) coutwho( ); / 2 p= / 5,p-who( ); / 6 first_obj.who( ); second_obj.who( ); 此程序的运行结果为: base base base First derivation Second derivation,例9-14:分析以下程序的执行结果。 #include class A public: virtual void show() cout“class A show() is called.“endl; ; class B:public A public: void show() cout“class B show() is called.“endl; ; void main() ,A demoA,*ptr; B demoB; ptr= 此程序的运行结果为: class A show() is called. class B show() is called.,例9-15:进一步的例子,先考虑下面的代码: #include #include class Creature public: char *KindOf() return “Creature“; ; class Animal : public Creature public: char *KindOf() ,return “Animal“; ; class Fish : public Creature public: char *KindOf() return “Fish“; ; void main() Animal animal; Fish fish; Creature *pCreature; Animal *pAnimal=,pCreature=pAnimal; coutKindOf(): “KindOf()KindOf(): “KindOf()KindOf(): “KindOf()KindOf(): “KindOf()endl; ,此程序的运行结果为: pAnimal-KindOf(): Animal pCreature-KindOf(): Creature pFish-KindOf(): Fish pCreature-KindOf(): Creature,例9-17:一个简单应用。 # include class figure protected:double x,y; public: void set_dim(double i,double j=0 ) x=i; y=j; virtual void show_area( ) cout“No area computation defined for this class.n“; ; ; class triangle:public figure public: void show_area( ) cout“Triangle with high“x“ and base“ y; cout“ has an area of “x*0.5*y“n“; ;,class square:public figure public: void show_area( ) cout“Square with dimension “x“*“y; cout“ has an area of “x*y“n“; ; class circle:public figure public: void show_area( ) cout“Circle with radius “x; cout“ has an area of “3.14*x*x“n“; ,; main ( ) figure *p; triangle t; square s; circle c; p= 此程序的类层次关系如图9-1所示。,图9-1 类层次关系示意图,返回本节,9.2.3 动态联编与虚函数,通过下面例9-19可以看到,派生类中对基类的虚函数进行替换时,要求派生类中说明的虚函数与基类中的被替换的虚函数之间满足如下条件: (1)与基类的虚函数有相同的参数个数; (2)其参数的类型与基类的虚函数的对应参数类型相同; (3)其返回值或者与基类虚函数的相同,或者都返回指针或引用,并且派生类虚函数所返回的指针或引用的基类型是基类中被替换的虚函数所返回的指针或引用的基类型的子类型。,总结动态联编的实现需要如下三个条件:,(1)要有说明的虚函数; (2)调用虚函数操作的是指向对象的指针或者对象引用;或者是由成员函数调用虚函数; (3)子类型关系的建立。,下面给出一个动态联编的例子。 例9-19:动态联编示例程序。 #include class Point public: Point(double i, double j) x=i; y=j; virtual double Area() const return 0.0; private: double x, y; ; class Rectangle:public Point public: Rectangle(double i, double j, double k, double l); /double Area() const return w*h; ,virtual double Area() const return w*h; private: double w, h; ; Rectangle:Rectangle(double i, double j, double k, double l):Point(i, j) w=k; h=l; void fun(Point ,例9-21:构造函数中调用虚函数示例。 #include class A public: A() virtual void f() cout“A:f() called.n“; ; class B : public A public: B() f(); void g() f(); ; class C : public B ,public: C() virtual void f() cout“C:f() called.n“; ; void main() C c; c.g(); 此程序的运行结果为: A:f() called. C:f() called.,返回本节,9.2.4 虚函数的限制,(1)在类体系中访问一个虚函数时,应使用指向基类类型的指针或对基类类型的引用,以满足运行时多态性的要求。 (2)在派生类中重新定义虚函数时,必须保证该函数的值和参数与基类中的说明完全一致,否则就属于重载(参数不同)或是一个错误(返回值不同)。 (3)若在派生类中没有重新定义虚函数,则该类的对象将使用其基类中的虚函数代码。 (4)虚函数必须是类的一个成员函数,不能是友元,但它可以是另一个类的友元。 (5)析构函数可以是virtual的虚函数,但构造函数则不得是虚函数。 (6)一个类的虚函数仅对派生类中重定义的函数起作用,对其他函数没有影响。,返回本节,9.2.5 虚函数与重载函数的比较,(1)重载函数要求函数有相同的返回值类型和函数名称,并有不同的参数序列;而虚函数则要求这三项(函数名、返回值类型和参数序列)完全相同。 (2)重载函数可以是成员函数或友元函数,而虚函数只能是成员函数。 (3)重载函数的调用是以所传递参数序列的差别作为调用不同函数的依据;虚函数是根据对象的不同去调用不同类的虚函数。 (4)虚函数在运行时表现出多态功能,这是C+的精髓;而重载函数则在编译时表现出多态性。,返回本节,9.3 纯虚函数和抽象类,9.3.1 纯虚函数 9.3.2 抽象类 9.3.3 虚析构函数,返回首页,9.3.1 纯虚函数,在许多情况下,在基类中不能给出有意义的虚函数定义,这时可以把它说明成纯虚函数,把它的定义留给派生类来做。定义纯虚函数的一般形式为: class 类名 virtual 返回值类型 函数名(参数表)= 0; ; 纯虚函数是一个在基类中说明的虚函数,它在基类中没有定义,要求任何派生类都定义自己的版本。纯虚函数为各派生类提供一个公共界面。,下面的代码在类Creature中将虚函数 KindOf 声明为纯虚函数: class Creature public: virtual char *KindOf()=0; ; char *Creature:KindOf() return “Creature“; ,使用下面的格式也是可以的: class Creature public: virtual char *KindOf()=0 return “Creature“; ;,例9-22:分析以下程序的运行结果。 #include #include class database public: int number; char goodsname20; float price; database(int n,char *s,float p) number=n; strcpy(goodsname,s); price=p; virtual void reporter()=0; ;,class reporter1:public database public: reporter1(int n,char *s,float p):database(n,s,p) void reporter() cout“number goodsname“endl; coutnumber“ “goodsnameendl; ; class reporter2:public reporter1 public:,reporter2(int n,char *s,float p):reporter1(n,s,p) void reporter() cout“number goodsname price“endl; coutnumber“ “goodsname“ “price; ; void main() reporter1 p1(100,“ink“,10.0); reporter2 p2(101,“pen“,20.0); p1.reporter(); p2.reporter(); ,此程序的运行结果为: number goodsname 100 ink number goodsname price 101 pen 20,返回本节,9.3.2 抽象类,如果一个类中至少有一个纯虚函数,那么这个类被称为抽象类(abstract class)。抽象类中不仅包括纯虚函数,也可包括虚函数。抽象类中的纯虚函数可能是在抽象类中定义的,也可能是从它的抽象基类中继承下来且重定义的。 抽象类有一个重要特点,即抽象类必须用作派生其他类的基类,而不能用于直接创建对象实例。 一个抽象类不可以用来创建对象,只能用来为派生类提供一个接口规范,派生类中必须重载基类中的纯虚函数,否则它仍将被看作一个抽象类。,例如: class point/*/; class shape / 抽象类 point center; public: point where( ) return center; void move(point p) enter=p; draw( ); virtual void rotate(int)=0; / 纯虚函数 virtual void draw( )=0; / 纯虚函数 ; shape x; / error,抽象类不能建立对象 shape *p; / ok,可以声明抽象类的指针 shape f( ); / error, 抽象类不能作为返回类型 void g(shape); / error, 抽象类不能作为参数类型 shape / ok,可以声明抽象类的引用,从基类继承来的纯虚函数,在派生类中仍是虚函数。 例如: class point /*/ ; class shape / 抽象类 point center; public: point where( ) return center; void move(point p) enter=p; draw( ); virtual void rotate(int)=0; / 纯虚函数 virtual void draw( )=0; / 纯虚函数 ;,class ab_circle:public shape /继承的 ab_circle:draw( )也是一个纯虚函数 /ab_circle类仍为抽象类 int radius; public: void rotate(int) ; ; 要使ab_circle成为非抽象类,必须作以下说明: class ab_circle:public shape int radius; public: void rotate(int) ; void draw( ); ;,例9-23:使用虚函数作出不同的销售报表示例。 #include #include class database public: int number; char goodsname20; float price; char sale; database(int n,char *s,float p,char a) number=n; strcpy(goodsname,s); price=p; sale=a; ,float couter() float t; t=price*number; return(t); virtual void reporter()=0; ; class reporter1:public database public: reporter1(int n,char *s

温馨提示

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

评论

0/150

提交评论