面向对象程序设计PPT课件 PPT资料共63页

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Stringed object vptr
Percussion object vptr
&Wind::play() & Stringed::play() & Percussion::play()
06.11.2019
第5章多态与抽象类
37
当通过对象调用虚函数时,是通过 vptr找到虚函数表,再找出虚函数的 真正地址。
06.11.2019
第5章多态与抽象类
28
【思考题5-4】将例5-5中用对象调用
虚函数,其结果如何?
【注意】只有通过对象指针或对象引 用来调用虚函数,才能实现动态联编 。如果采用对象来调用虚函数,则采 用的是静态联编方式。
06.11.2019
第5章多态与抽象类
29
【例5-6】将例5-4基类的成员函数 print()设为虚函数,采用对象引用调 用虚函数,进而实现动态联编。
06.11.2019
第5章多态与抽象类
32
小结
(1)如果你期望在派生类中重新定义一 个成员函数,那么你就应该在基类中 把该函数设为virtual。
(2)以单一指令调用不同函数,这种性 质就是"多态"。
(3)虚函数是C++语言的多态性质和动 态绑定的关键。
(4)虚函数派生下去仍是虚函数,而且
可以省略virtual关键词。 06.11.2019
25
该程序的运行结果为:
A student A graduate student A student A student A graduate student
06.11.2019
第5章多态与抽象类
26
【思考题5-5】如果将例5-5中Student类改 为:
ቤተ መጻሕፍቲ ባይዱ
class Student
{
public:
在替代之后,派生类对象就可以作为 基类的对象使用,但只能访问从基类
继承的成员。 06.11.2019
第5章多态与抽象类
9
【例5-3】示例类型兼容规则的应用。 教材P261,该程序的运行结果为:
Base class Base class Base class Derivel class Derive2 class
06.11.2019
第5章多态与抽象类
4
在主函数中建立了4个不同类的对象,进行 了类似的操作,重复写了4遍类似的语句。
应该怎样有效地来处理它们同样的行为呢 ?我们首先想到的是用循环解决,但调用 这些同名函数的对象却不相同。
分析这些对象有一个共同点,那就是来自
于同一个基类,而基类与派生类对象有什
《面向对象程序设计》
第5章 多态与抽象类
内容提要
5.1 类层次的多态问题 5.2 类型兼容规则(重点) 5.3 多态的概念 5.4 多态的分类 5.5 联编 5.6 虚函数(重点) 5.7 抽象类与纯虚函数(重点)
提出问题
【例5-1】在例4-1程序中存在的两个 不足:
06.11.2019
第5章多态与抽象类
30
运行结果表明,只要定义一个基类的 对象指针或对象引用,就可以调用期 望的虚函数。
思考题:如果将对象引用或对象指针换 为对象,那么是静态联编。
06.11.2019
第5章多态与抽象类
31
在实际应用中,编程人员不必过多地 考虑类的层次关系,无须显式地写出 虚函数的路径,只须将对象指针指向 相应的派生类或引用相应的对象,通 过动态联编就可以对消息做出正确的 反应。
么关系呢?事实上,它们遵循类型兼容规
则。 06.11.2019
第5章多态与抽象类
5
Employee *emp[4]={&m1,&t1,&s1,&sm1}; //声明 抽象类指针数组
for(int i=0;i<4;i++)
{
emp[i]->Pay(); //单一指令,实现多态, 计算指针指向对象的工资
06.11.2019
第5章多态与抽象类
23
在程序运行时,不同类的对象调用各 自的虚函数,这就是运行时多态。
06.11.2019
第5章多态与抽象类
24
【例5-5】将例5-4基类的成员函数 print()设为虚函数,采用对象指针调 用虚函数,进而实现动态联编。
06.11.2019
第5章多态与抽象类
06.11.2019
第5章多态与抽象类
10
根据类型兼容规则,基类指针p可以指 向派生类对象obj2、obj3(即派生类 对象可以代替基类对象,给基类指针 赋值),但编译时,编译器根据p的类 型是基类型,自动调用基类的who(), 所以结果如上所示。
06.11.2019
第5章多态与抽象类
11
【思考题5-3】如果基类指针要访问派 生类的who(),怎么办?
06.11.2019
第5章多态与抽象类
19
5.6 虚函数
虚函数就是在基类中被关键字virtual 说明、并在一个或多个派生类中被重 新定义的成员函数。
06.11.2019
第5章多态与抽象类
20
声明虚函数的格式如下:
virtual <函数值类型> <函数名>(<参数表>);
06.11.2019
06.11.2019
第5章多态与抽象类
35
每一个含有虚函数的类,编译器都为 它做一个虚函数表,表中的每一项都 指向一个虚函数的地址,实现上是一 个函数指针的数组。此外,编译器也 会为类加上一个数据成员,是一个指 向该虚函数表的指针(vptr)。
06.11.2019
第5章多态与抽象类
36
Wind object vptr
virtual void print(); //虚函数的声明
};
virtual void Student::print() //虚函数的实现
{
cout<<"A student"<<endl;
}06.11.2019
第5章多态与抽象类
27
注意:当有虚函数声明时,virtual关 键字只用在虚函数的声明中,不能用 在虚函数定义中。
C++提供了多态机制来解决这个问题。 类型兼容规则是C++多态的重要基础。
06.11.2019
第5章多态与抽象类
12
多态
多态(Polymorphism)是指具有相似 功能的不同函数使用同一个名称来实 现,从而可以使用相同的调用方式来 调用这些具有不同功能的同名函数的 特性。
06.11.2019
所以,将构造函数声明为虚函数是没有意
义的。
06.11.2019
第5章多态与抽象类
43
(3)内联成员函数不能声明为虚函数。 因为内联函数的执行代码是明确的, 在编译时已被替换,没有多态性的特 征。如果将那些在类声明时就定义内 容的成员函数声明为虚函数,此时函 数不是内联函数,而以多态性出现。
06.11.2019
06.11.2019
第5章多态与抽象类
17
可见,同一个指针,在不同阶段被绑 定的类对象将是不同的,进而被关联 的类成员函数也是不同的。
如何来确定是用静态联编还是用动态 联编呢?
C++规定,动态联编通过继承和虚函数 来实现。
06.11.2019
第5章多态与抽象类
18
虚函数是动态联编的基础。下面介绍 虚函数。
至于基类指针或引用指向派生类对象 时,调用的是派生类虚函数,其奥妙 在于虚函数表以及一种间接调用方式 :
06.11.2019
第5章多态与抽象类
38
虚函数表中的内容依据类中的虚函数 声明次序,一一填入函数指针。
派生类会继承基类的虚函数表(以及
其它所有可以继承的成员),我们在
派生类中改写虚函数时,虚函数表也
员。这样,公有派生类实际就具备了 基类的所有功能,凡是基类能解决的
问题,公有派生类都可以解决。
06.11.2019
第5章多态与抽象类
8
类型兼容规则中所指替代包括以下情 况:
(1)派生类的对象可以赋值给基类的对象。
(2)派生类的对象可以初始化基类的引用。
(3)派生类的对象的地址可以赋值给基类的指 针变量。
第5章多态与抽象类
44
(4)析构函数可以是虚函数,且往往被 定义为虚函数。一般来说,若某类中 有虚函数,则其析构函数也应当定义 为虚函数。
06.11.2019
第5章多态与抽象类
45
特别是需要析构函数完成一些有意义
的操作,如释放内存时,由于实施多
态性时是通过将基类的指针指向派生
类的对象来完成的,如果删除该指针
(1)基类Pay()和Display()的函数体均为空, 在实现部分仍要写出函数体,显得冗余。
(2)在主函数中,建立了4个不同类的对象, 进行了类似的操作,重复写了4遍类似的语句 ,程序不够简洁。
06.11.2019
第5章多态与抽象类
3
分析问题
基类设置成员函数Pay()和Display() 的目的是为了统一规定类簇的基本行 为,虽有冗余,但也必要。为此,C++ 提供了纯虚函数来解决此问题。
06.11.2019
第5章多态与抽象类
42
(1)静态成员函数不能声明为虚函数。因为 静态成员函数不属于某一个对象,没有多
态性的特征。
(2)构造函数不能是虚函数。构造函数是在
定义对象时被调用,完成对象的初始化,
此时对象还没有完全建立。虚函数作为运
行时的多态性的基础,主要是针对对象的
,而构造函数是在对象产生之前运行的。
受到了影响,表中每一项所指向函数
地址将不再是基类中的函数地址,而
是派生类的函数地址。
06.11.2019
第5章多态与抽象类
39
因此,一个指向Student对象的指针, 所调用的print函数就是 Student::print(), 而一个指向 GStudent对象的指针,所调用的print 函数就是GStudent::print()。
静态联编 动态联编
06.11.2019
第5章多态与抽象类
15
静态联编是在编译阶段完成的联编。 例5-2、例5-3及以前的函数重载都是 采用静态联编方式。
例5-4,教材P265,是静态联编。
动态联编是在运行阶段完成的联编。
06.11.2019
第5章多态与抽象类
16
在例5-4中,静态联编把基类指针ps指 向的对象绑定到基类上,而在运行时 进行动态联编将把ps指向的对象绑定 到派生类上。
第5章多态与抽象类
21
在派生类中重新定义虚函数时,其函 数原型包括返回类型、函数名、参数 个数与参数类型的顺序,都必须与基 类中的原型必须相同。
06.11.2019
第5章多态与抽象类
22
一个函数一旦被声明为虚函数,则无 论声明它的类被继承了多少层,在每 一层派生类中该函数都保持虚函数特 性。因此,在派生类中重新定义该函 数时,可以省略关键字virtual。但是 ,为了提高程序的可读性,往往不省 略。
,就会调用该指针指向的派生类的析
构函数,而派生类的析构函数又自动
调用基类的析构函数,这样整个派生
类的对象才被完全释放。
06.11.2019
第5章多态与抽象类
46
因此,析构函数常被声明为虚函数。 如果一个类的析构函数是虚函数,那 么,由它派生的所有子类的析构函数 也是虚函数。
第5章多态与抽象类
33
【思考题5-8】虚函数与虚拟继承有什 么相似的地方?他们之间有没有联系 ?
如果能了解C++编译器对于虚函数的实 现形式,我们就能知道为什么虚函数 可以实现动态绑定。下面介绍虚函数 表。
06.11.2019
第5章多态与抽象类
34
5.6.3 虚函数表
为了达到动态绑定的目的,C++编译器 通过某个表格,在执行期间“间接” 调用实际上欲绑定的函数,这样的表 格称为虚函数表(vtable)。
emp[i]->Display();//单一指令,实现多态, 输出指针指向对象的信息
}
06.11.2019
第5章多态与抽象类
6
5.2类型兼容规则
类型兼容规则是指在需要基类对象的 任何地方,都可以使用公有派生类的 对象来替代。
06.11.2019
第5章多态与抽象类
7
通过公有继承,派生类得到了基类中 除构造函数、析构函数之外的所有成
06.11.2019
第5章多态与抽象类
40
将一个类的成员函数定义为虚函数有 利于编程,尽管它会引起一些额外的 开销。
那么,是否所有成员函数都可以声明 为虚函数呢?
06.11.2019
第5章多态与抽象类
41
一般来说,可将类簇中具有共性的成 员函数声明为虚函数,而具有个性的 函数没有必要声明为虚函数。但是下 面的情况例外:
第5章多态与抽象类
13
5.4 多态的分类
C++支持的多态可以分为四种类型:
重载多态:函数重载和运算符重载 强制多态:强制类型转换 包含多态:虚函数 参数多态:函数模板和类模板
06.11.2019
第5章多态与抽象类
14
联编
联编是指把一个标识符名和一个存储 地址联系在一起的过程。即函数调用 与某个函数在多态的实现过程中,确 定调用哪个同名联系的过程,又称绑 定。分为:
相关文档
最新文档