第九章 多态性与虚函数
合集下载
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
引用,使该指针指向该基类的不同派生类的对象,并通过
该指针指向虚函数,才能实现动态的多态性。
9.2.2 虚函数
5.内联函数因为每个对象有独立的一份函数代码,无映射 关系,不能作为虚函数。 6.析构函数可定义为虚函数,构造函数不能定义虚函数, 因为在调用构造函数时对象还没有完成实例化。在基类中及 其派生类中都动态分配的内存空间时,必须把析构函数定义
9.3 抽象类
9.3.1 纯虚函数 9.3.2 抽象类 9.3.3 继承与多态的应用 9.3.4 虚函数表
9.3.5 重载、覆盖和隐藏
9.3.1 纯虚函数
纯虚函数:
纯虚函数(pure virtual function)是 指被标明为不具体实现的虚拟成员函数。它 用于这样的情况:定义一个基类时,会遇到 无法定义基类中虚函数的具体实现,其实现 依赖于不同的派生类。
第九章 多态性与虚函数
学习目标: 1.理解多态性的概念、多态的类型;
2.掌握用虚函数实现动态联编;
3.理解静态多态性与动态多态性的区别与实
现机制;
4.掌握抽象类的概念、设计方法与应用。
第九章 多态性与虚函数
9.1 多态性概述 9.2 多态性与虚函数
9.3 抽象类
9.4 抽象类实例 9.5 程序实例
针,就可以使属于不同派生类的不同对象产生不同的行为,
从而实现了运行过程的多态。
9.2.2 虚函数 虚函数的定义:
virtual
函数体
返回类型
函数名(参数表){
}
9.2.2 虚函数 成员函数设置为虚函数的要点:
1.派生类中定义虚函数必须与基类中的虚函数同名外, 还必须同参数表,同返回类型(或复合类型兼容规则的返 回类型)。否则被认为是重载,而不是虚函数。 2.只有类的成员函数才能说明为虚函数。这是因为虚函 数仅适用于有继承关系的类对象。 3.静态成员函数,是所有同一类对象共有,不受限于某 个对象,不能作为虚函数。 4.实现动态多态性时,必须使用基类类型的指针变量或
为虚函数,实现撤消对象时的多态性。
7.如果定义放在类外,virtual只能加在函数声明前面, 不能(再)加在函数定义前面。正确的定义必须不包括 virtual。 8.如果基类的某个成员函数被说明为虚函数,它无论被公 有继承多少次,它仍然保持其虚函数的特性。
9.2.2 虚函数
Point
使用虚函数是实现动态联编的
9.1多态性概述
多态是指同样的消息被不同类型的对
象接收时导致不同的行为,所谓消息是指
对类的成员函数的调用,不同的行为是指
不同的实现,也就是调用了不同的函数。
9.1多态性概述
9.1.1 多态的类型
9.1.2 多态的实现
9.1.1多态的类型
类型 名称 应用
专用 多态
普通函数及类的成员函数的重载、 重载多态 运算符重载 强制多态 不同类型数据运算 包含多态 虚函数 参数多态 类模板
9.2.2 虚函数
有了虚函数,改写8.3节[例8-4]中class B0类的 display()函数,将其定义成虚函数。程序的其余部分不 要作任何改动。 class B0{ public: virtual void display(){ cout<<"B0::display()"<<endl;} //公有成员函数}; 程序运行结果为: //基类B0声明
时,派生类的虚函数便覆盖了基类的虚函数。不仅如此,
派生类中的虚函数还会隐藏基类中同名函数的所有其他 重载形式。
9.2.2 虚函数
在[例9-1]的fun函数中同一条语句 p->Area()
被执行了2次,但执行的结果却不相同。这就
是 “同一界面,不同实现”,或者说“同样消 息,不同行为”的含义。这充分反映了类的多态 性的可扩展能力。
通用 多态
9.1.2 多态的实现
多态从实现的角度来讲可以划分为两类:编译时的多 态和运行时的多态。
多态类型 编译时的多态(静 态联编) 运行时的多态(动 态联编)
完成时刻 程序编译时刻确 定同名操作对象 程序运行时刻确 定同名操作对象
应用 运算符重载、函 数模板实例化 虚函数
9.2 多态性与虚函数
9.3 抽象类
带有纯虚函数的类是抽象类,抽象类是一种特 殊的类,其作用是为一个类族提供统一的操作界 面。抽象类是为了抽象和设计的目的而建立的。 建立抽象类,就是为了通过它多态地使用其中的
成员函数。 一个抽象类不能实例化。抽象类处于类族的 上层,只能通过继承机制,生成抽象类的非抽象 派生类,然后再将派生类实例化。
B0::display()
B1::display() D1::display()
9.2.2 虚函数 虚函数与运行时的多态性: 【计算学分1】使用指针。可由本科生类派生
出研究生类,但它们各自的从课程学时数折算 为学分数的算法是不同的,本科生是16个学 时一学分,而研究生是20个学时一学分。 【计算学分2】使用引用。
9.2.3 虚析构函数
在C++中,不能定义虚构造函数。但可以定义 虚析构函数,而且通常将析构函数声明为虚函数。 虚析构函数定义形式如下: virtual ~类名(){ 函数体 }; 当基类的析构函数被声明为虚函数,则派生 类的析构函数,无论是否使用virtual关键字进 行声明,都自动成为虚函数。
9.2.1静态联编
静态联编实例
Rectangle::Rectangle(double i, double j, double k, double l) :Point(i,j) { w=k; h=l; } void fun(Point &s) { cout<<"Area="<<s.Area()<<endl; } void main() { Rectangle rec(3.0, 5.2, 15.0, 25.0); fun(rec); 调用的是Point类 } 的Area() 运行结果: Area=0
9.2.3虚析构函数
一般来说,如果使用基类指针指向由new运算建立的对 象,而delete又作用于指向派生类对象的基类指针,就 要将基类的析构函数声明为虚析构函数,让所有派生类 的析构函数自动成为虚析构函数。虚析构函数常用于动 态分配内存时。 [例9-2]用虚析构函数删 除派生类动态对象。 程序运行结果: A::A() is called B::B() is called B::~B() is called A::~A() is called 在此例中如果基类的析构 函数不是虚析构函数,则 程序运行结果如下: A::A() is called B::B() is called A::~A() is called
多态类型多态类型完成时刻完成时刻应用应用编译时的多态静态联编程序编译时刻确定同名操作对象运算符重载函数模板实例化运行时的多态动态联编程序运行时刻确定同名操作对象虚函数92多态性与虚函数922虚函数922虚函数921静态联编923虚析构函数921静态联编includelt
第九章 多态性与虚函数பைடு நூலகம்
多态性(polymorphism):
9.3.3 继承与多态的应用——单链表派生类
[例9-4]采用抽象类技术实现通用单链表。
1.改造【例7-8】的头文件,不采用模板类,而采用虚 函数实现多态性,达到通用的目的。结点类数据域被改 造为指针,而把数据放在一个抽象类中,由指针与之建 立联系。
Node Info AbsObject* link Node* InsertAfter():void RemoveAfter():Node* Linkinfo(obj:AbsObject*):void „ 指向下一结点 <<abstract>>operator>(obj:AbsObject&):bool <<abstract>>operator!=(obj:AbsObject&):bool <<abstract>>~AbsObject() „ AbsObject的派生类
9.3.2 抽象类
抽象类不能实例化,即不能定义一个抽象类的 对象,但是,我们可以声明一个抽象类的指针和引 用。通过指针或引用,我们就可以指向并访问派生 类对象,进而访问派生类的成员,这种访问是具有 多态特征的。 [例9-3]抽象类应用 此例中,先定义一个形状(Shape)抽象类,其中 包含有两个公共接口:area()和show()虚函数。该 类派生出点(Point)、圆(Circle)、圆柱体 (Cylinder) 3个具体的类,并给出各自的接口的实 现。
基础,但必须同时满足三个条件:
1.满足类型兼容规则; 2.在基类中定义虚函数,在派生
-X : int -Y : int +Point(x,y)() +<<virtual>>Area() : double
类中重新定义虚函数;
3.要由成员函数来调用,或通过 指针、引用来调用虚函数。 [例9-1]
Circle -radius : double +Circle(int x,int y,double r)() +Area() : double
9.2.1 静态联编 9.2.2 虚函数
9.2.3
虚析构函数
9.2.1静态联编
静态联编实例
#include<iostream> using namespace std; 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; };
9.2.2 虚函数
根据类型兼容规则,可以使用派生类的对象代替基类 对象。如果用基类类型的指针指向派生类对象,就可以通 过这个指针来访问该对象。问题是访问到的只是从基类继 承来的同名成员。 解决办法:如果需要通过基类的指针指向派生类的 对象,并访问某个与基类同名的成员,那么首先在基类中 将这个同名函数说明为虚函数。这样,通过基类类型的指
9.2.2 虚函数
使用虚函数还必须注意以下几点: ①当在派生类中未重新定义虚函数,或者定义了虚函数的重 载函数,但并没有重新定义虚函数时,这时无法通过派生类指 针、引用调用派生类的虚函数。调用的仍然是基类的虚函数, 属于静态联编。 ②当基类构造函数调用虚函数时,不会调用派生类的虚函数。 ③派生类重写继承来的虚函数时,如果函数有默认形参值, 不要重新定义不同的值。 只有虚函数是动态联编的,如果派生类需要修改基类的行为 (即重写与基类函数同名的函数),就应该在基类中将相应的 函数声明为虚函数。而基类中声明的非虚函数,通常代表那些 不希望被派生类改变的功能,也是不能实现多态的。虽然语法 对此没有强行限制,一般不要重写继承而来的非虚函数。
9.3.2 抽象类
抽象类的主要作用是通过它为一个类族建立一 个公共的接口,使它们能够更有效地发挥多态特 性。抽象类声明了一族派生类的共同接口,而接 口的完整实现,即纯虚函数的函数体,则由派生 类根据需要自行定义。 抽象类派生出新的类之后,如果派生类给出 所有纯虚函数的函数实现,这个派生类就可以定 义自己的对象,因而不再是抽象类;反之,如果 派生类没有给出全部纯虚函数的实现,这时的派 生类仍然是一个抽象类。
程序的运行结果为: 0 1256.64
9.2.2 虚函数
判断派生类的一个函数成员是否是虚函数的规则: 1.该函数是否与基类的虚函数名称相同;
2.该函数是否与基类的虚函数参数个数相同、对应参
数类型相同; 3.该函数是否与基类的虚函数返回值类型相同,或者
满足类型兼容规则的指针、引用型的返回类型。
如果从名称、参数及返回值三个方面检查之后,派生 类的函数满足了上述条件,就会自动确定为虚函数。这
多态性是面向对象程序语言的一个重要特征。通过多 态,实现同一界面,多种实现。若程序设计语言不支持多 态性,不能称为面向对象的语言。 C++还提供一种更为灵活的多态机制:虚函数(Virtual function)。虚函数允许函数调用与函数体的联系在运行时 才进行。 类、继承和多态,提供了软件重用性和扩展性需要的 卓越表达能力。
纯虚函数的定义:
virtual 返回类型 函数名(参数表)=0;
9.3.1 纯虚函数 定义纯虚函数的要点:
1.定义纯虚函数时,不能定义虚函数的实现部分。 即使是函数体为空也不可以,函数体为空就可以执 行,只是什么也不做就返回。而纯虚函数不能调用。 2.“=0”表明程序员将不定义该函数,函数声明是 为派生类保留一个位置。“=0”本质上是将指向函数 体的指针定为NULL。 3.在派生类中必须有重新定义的纯虚函数的函数 体,这样的派生类才能用来定义对象。