《面向对象程序设计语言C++第07章_虚函数和多态性课件》由会员分享,可在线阅读,更多相关《面向对象程序设计语言C++第07章_虚函数和多态性课件(50页珍藏版)》请在金锄头文库上搜索。
1、1,面向对象程序设计语言C+,电子科技大学计算机学院,2,第七章 虚函数和多态性,C+使用多态性实现同一个消息,不同接收者采取不同的响应方式的这种现象。 多态性是一个事务有多种形态。 在面向对象语言中,一般这样描述多态:向不同对象发送同一个消息,不同的对象在接收时会产生不同的行为。也就是说,每个对象可以用自己的方式去响应共同的消息。,3,第七章 虚函数和多态性,C+语言的多态性有两种类型: 静态多态性和动态多态性。 函数重载和运算符重载都是静态的多态性。 在程序编译时系统就能够决定调用哪个函数,因此静态多态性又称为编译时的多态性。动态多态性时程序运行过程中才动态的确定操作所针对的对象。它又称为
2、运行时的多态性。动态多态性是通过虚函数实现的。,4,第七章 虚函数和多态性,7.1 虚函数 7.1.1静态多态性 对于普通成员函数的重载,可表达为下面的方式: (1)在同一个类中重载; (2)在不同类中重载; (3)基类的成员函数在派生类中重载; 因此,重载函数的访问是在编译时区分的,这种程序运行之前就能够在多个函数中确定当前访问的函数的方法称为静态多态性。,5,第七章 虚函数和多态性,7.1 虚函数 7.1.1静态多态性 有以下三种区分方法: 据参数的特征加以区分,例如: Show(int, char) 与 Show(char * , float) 使用”:”加以区分,例如: Circle
3、: Show 有别于 Point : Show 根据类对象加以区分 ACircle.Show() 调用 Circle : Show(),6,第七章 虚函数和多态性,7.1 虚函数 7.1.1静态多态性 子类可以重载父类的成员: class A public: void fun() coutIn Aendl; ; class B : public A public: void fun() coutIn Bendl; ;,7,第七章 虚函数和多态性,7.1 虚函数 7.1.1静态多态性 C Cobj; Cobj.fun(); /调用C:fun() Cobj.B:fun(); /调用B:fun()
4、Cobj.A:fun(); /调用A:fun() A /调用A:fun(),8,第七章 虚函数和多态性,7.1 虚函数 7.1.2基类和派生类的指针与对象的关系 (1)可以用指向基类的指针指向其公有派生类的对象 基类指针访问的是派生对象的拥有的基类部分, 派生类自身的部分不能被基类指针访问。 指向派生类的指针指向基类的对象是不正确的 (2)希望用基类指针访问其公有派生类的特定成员, 必须将基类指针用显示类型转换为派生类指针。,9,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 1虚函数的概念 一个指向基类的指针可用来指向从基类公有派生的任何对象。 是 C+ 实现运行时多态性
5、的关键途径。 如果有多个或者多层派生类,通过一个基类指针可以访问所有派生类对象的成员函数,这样就可以实现一个接口,多个实现的访问了。,10,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 class Base public: Base(int a) x=a; void who() cout base xn; protected: int x; ;,class First_d: public Base public: First_d (int a ):Base(a) void who() cout First derivation xn; ; class Sec_d :pub
6、lic Base public: Sec_d (int a):Base(a) void who() cout Second derivation xn; ;,11,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 Base * p; Base base_obj(1); First_d first_obj(2); Sec_d second_obj(3); p= 输出?,12,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 程序的输出是: base 1 base 2 base 3 调用的都是父类的函数版本,13,第七章 虚函数和多态性,7.1 虚函数 7.1.
7、3 虚函数与多态性 通过父类指针来看,该指针所指向的是父类对象。,14,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 如果随着p所指向的对象的不同,p-who()能调用不同类中who()的版本, 这样就可以用一个界面访问多个实现版本。 实际上,这表达了一种动态的性质,函数调用依赖于运行时p所指向的对象。 虚函数提供的就是这种解释机制。 虚函数是在基类中被冠以virtual的成员函数,它提供了一种接口界面。,15,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 虚函数可以在一个或多个派生类中被重新定义,但要求在派生类中重新定义时, 虚函数的函数原型(包括
8、返回类型,函数名,参数个数,参数类型的顺序)必须完全相同。 重写上例。,第七章 虚函数和多态性,7.1 虚函数 7.1.3 虚函数与多态性 class Base public: Base(int a) x=a; virtual void who() cout base xn; protected: int x; ;,16,class First_d: public Base public: First_d (int a ):Base(a) void who() cout First derivation xn; ; class Sec_d :public Base public: Sec_d
9、(int a):Base(a) void who() cout Second derivation xn; ;,17,第七章 虚函数和多态性,7.1 虚函数 2运行时的多态性与虚特性 (1)运行时的多态性 在带有虚函数的类中,编译器设置一个指针,称为虚指针vpointer(缩写为VPTR)。 编译器对每个包含虚函数的类创建一个虚表(称为VTABLE),存放类的虚函数地址。 VPTR指向这个对象的VTABLE。 通过基类指针做虚函数调用时,编译器取得这个VPTR,找到该类的VTABLE,并在其中查找相应虚函数地址,完成动态匹配。,18,第七章 虚函数和多态性,7.1 虚函数,单界面、多实现,cl
10、ass figure protected: float x,y; public: void set_dim(float i, float j=0) x=i; y=j; virtual void show_area() cout “No area n”; ;,class triangle : public figure public: void show_area() cout x* 0.5* y “n”; ;,class square : public figure public: void show_area() cout x * y “n”; ;,class circle : public
11、 figure public: void show_area() cout 3.14 * x * x; ;,void main() figure * p; triangle t;square s;circle c; p= base()coutbase()endl; ; class derived:public base public: derived()coutderived()endl; derived()coutderived()endl; ;,int main() base *pb=new derived; delete pb; return 0; ,32,第七章 虚函数和多态性,7.1
12、 虚函数 请问程序的输出是什么?,程序的输出如下: base() derived(); base(); 构造函数base()和derived()都被调用了,但是析构函数只有base()调用了。,33,第七章 虚函数和多态性,7.1 虚函数 原因很简单:基类指针只调用基类成员函数,不能够调用派生类成员函数,即使是析构函数也是如此。如果希望能够执行派生类的析构函数,则需要将基类的析构函数声明为虚析构函数: virtual base() coutbase()endl; 当基类的析构函数声明为虚函数时,无论指针指向的是同一类族中的哪一个对象,当对象撤销时,系统会采用动态关联,调用相应的析构函数,对该对
13、象进行清理工作。,如果希望通过基类指针或者引用访问派生类成员函数, 但基类功能比较抽象或者不能确定功能, 可以将基类定义为抽象类,即只定义函数名字,没有函数体,具体功能由派生类添加,35,第七章 虚函数和多态性,7.2 纯虚函数和抽象类 基类往往表示一些抽象的概念。 例如,shape是一个基类,它表示具有形状的东西,从shape可以派生出封闭图形和非封闭图形两个派生类。 封闭图形又可以派生出椭圆形、多边形, 这个类等级的基类shape体现了一个抽象的概念 在shape中定义一个求面积的函数显然是无意义的 但可以将其说明为虚函数,提供各派生类一个公共的界面,并由各派生类提供求面积函数的各自版本,
14、36,第七章 虚函数和多态性,7.2 纯虚函数和抽象类 基类的有些虚函数没有定义是很正常的,但是要求派生类必须重定义这些虚函数,以使派生类有意义。为此,C+ 引入了纯虚函数的概念。 纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,要求任何派生类都必须定义自己的版本。为说明一纯虚函数,使用下列一般形式: virtual type func_name(参数表) = 0;,37,第七章 虚函数和多态性,7.2 纯虚函数和抽象类 将一虚函数说明成纯虚函数,就要求任何派生类都应该定义自己的实现。 在构造函数和析构函数中调用虚函数时,采用静态联编,因此,在构造函数和析构函数中不能够调用纯虚函数。
15、但其他的成员函数可以调用纯虚函数。 如果一个类至少有一个纯虚函数,那么就称该类为抽象类。抽象类机制支持一般概念的表示。 抽象类只能用作其他类的基类,抽象类不能建立对象。抽象类不能用作参数类型、函数返回类型或显式转换的类型。但可以声明抽象类的指针和引用。,38,第七章 虚函数和多态性,7.2 纯虚函数和抽象类 纯虚函数和抽象类的例子: class shape public: virtual void rotate( int )=0; virtual void draw( )=0; ; ,用面向对象方法实现一个异质链表。 异质是指链表中各表项内容的类型不要求相同,(1)以大学环境为例,这里包括学生
16、、职员和教师。 希望对这些人的信息进行管理(所有的人员信息记录在一个链表中)。,学生:姓名、年龄、身份证号码、平均成绩。 职员:姓名、年龄、身份证号码、小时工资。 教师:姓名、年龄、身份证号码、年工资。,要求能实现三个操作: 插入。向异质链表中增加一个学生、职员或教师的信息。 删除。从链表中删除一个学生、职员和教师的信息。 打印。显示链表中所有的信息。,(2)对于一些既是学生又是教师双重身份的人,应能单独记录之。 (3)当以上系统设计完成后,如果希望增加其他人员的派生类(如院长、校长或系主任等),望能给出相应的派生类。,class Person char * name;int age;char * id; public: vo