第10章 虚函数与多态性.

Slides:



Advertisements
Similar presentations
课程 要求 参考 书目 课程 内容 课程 练习.
Advertisements

程序设计实习 第十五讲 多 态.
第6章 多态性与虚函数.
7.2 访问控制 —— 公有继承 公有继承练习 //Point.h #ifndef _POINT_H #define _POINT_H class Point { //基类Point类的定义 public: //公有函数成员 void initPoint(float x = 0, float.
内容提要 代码重用 类的继承 多态 抽象类 多重继承 虚拟继承. 常宝宝 北京大学计算机科学与技术系
预备知识——C++、类与对象 C++要素 类和对象 模版 类的继承.
第12章 组合与继承 欢迎辞 第14次见面!.
面向对象程序设计 第三章 C++面向对象程序设计 武汉大学 赵小红.
第14章 c++中的代码重用.
C++语言程序设计 第七章 继承与派生 清华大学 郑 莉.
C++ 面对对象程序设计 授课老师:.
第11章 类的继承和派生 继承是面向对象程序设计方法的四个基本特征之一,是程序代码可重用性的具体体现。
C++中的声音处理 在传统Turbo C环境中,如果想用C语言控制电脑发声,可以用Sound函数。在VC6.6环境中如果想控制电脑发声则采用Beep函数。原型为: Beep(频率,持续时间) , 单位毫秒 暂停程序执行使用Sleep函数 Sleep(持续时间), 单位毫秒 引用这两个函数时,必须包含头文件
全国计算机等级考试 二级基础知识 第二章 程序设计基础.
第6章 多态性与虚函数.
第10讲 Java面向对象编程基础(4) 教学目标 主要内容.
Using C++ The Weird Way Something about c++11 & OOP tricks
資料大樓 --談指標與陣列 綠園.
Derived Class 前言 衍生類別的定義 單一繼承 public, protected, 和 privated 基底類別
面向对象技术 练习2 ffh.
版权所有 复制必究 第 3 章 C++面向对象程序设计.
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
第12讲 多继承与虚基类 多继承 虚基类.
管理信息结构SMI.
辅导课程六.
第9章 类和对象(一) 9.1 面向对象的基本概念 9.2 类与对象的声明和定义 9.3 成员函数 9.4 对象的访问 9.5 对象的存储.
第一单元 初识C程序与C程序开发平台搭建 ---观其大略
第八章 继承与派生 丘志杰 电子科技大学 计算机学院 软件学院.
泛型委托 泛型接口、方法和委托.
第十一章 继承和派生. 主讲教师:全红艳 第十一章 继承和派生.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
用event class 从input的root文件中,由DmpDataBuffer::ReadObject读取数据的问题
第七章 操作符重载 胡昊 南京大学计算机系软件所.
第16章 虛擬與多形 16-1 虛擬函數 16-2 純虛擬函數與抽象類別 16-3 多形 16-4 虛擬繼承與虛擬解構子.
六、函数 教学目标: 函数的概念、定义、调用和返回 带自定义函数的程序设计 递推算法 递归思想及算法实现 函数的参数传递方式 C语言程序设计.
C++语言程序设计 C++语言程序设计 第七章 类与对象 第十一组 C++语言程序设计.
C++大学基础教程 第11章 多态性 北京科技大学 信息基础科学系 2019/4/8 北京科技大学.
C语言程序设计 主讲教师:陆幼利.
简单介绍 用C++实现简单的模板数据结构 ArrayList(数组, 类似std::vector)
第11讲 类的继承 1. 类的继承的概念 2. 类的单继承机制 3. 单继承中的构造函数和析构函数.
$9 泛型基础.
潘爱民 C++ Overview 潘爱民
C++复习3 ----类的继承与派生.
C#面向对象程序设计 $6 深入理解类.
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
第13讲 多态 友员函数 多态性与虚函数 纯虚函数和抽象类.
补课.
C++程序设计— 多态与虚函数 主讲:资讯系张玉宏.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本节内容 类成员的访问控制 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
多层循环 Private Sub Command1_Click() Dim i As Integer, j As Integer
C++语言程序设计 C++语言程序设计 第六章 指针和引用 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
辅导课程十五.
第7章 模板 陈哲 副教授 南京航空航天大学 计算机科学与技术学院.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第十一章 异常处理 C++语言程序设计.
谭浩强编著 C++面向对象程序设计 授课教师:姬广永 学习网站:
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第八章 继承 C++语言程序设计.
基于列存储的RDF数据管理 朱敏
本节内容 动态链接库 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
本课程学习目标 培养编程技能 开启智慧之门.
C++语言程序设计 C++语言程序设计 第十章 多态 第十一组 C++语言程序设计.
C++语言程序设计 C++语言程序设计 第九章 类的特殊成员 第十一组 C++语言程序设计.
資料結構與C++程式設計進階 C++與資料結構 講師:林業峻 CSIE, NTU 7/ 5, 2010.
本节内容 this指针 视频提供:昆山爱达人信息技术有限公司 官网地址: 联系QQ: QQ交流群 : 联系电话:
Presentation transcript:

第10章 虚函数与多态性

10.1 共同的基类 10.1.1 概念中的共性 在面向对象的程序设计中,每个类都是对一种概念的描述。通常,一些概念之间存在着共性,这种共性又体现了新的概念,得到这种新概念的过程就是抽象。 class Rectangle class Circle 除了少量的特殊方法外,Rectangle与Circle有着完全相同的一组方法,只是方法实现的过程又不相同。

10.1 共同的基类(续) 10.1.2 公共基类 1. 利用公共基类完善概念关系,消除代码冗余 2. 简单抽象中存在的问题 将所有图形元素的共性抽象出来,形成一种新的公共基类Shape,使Rectangle与Circle等由此基类派生。 重新描述的概念关系使代码得到了简化,Rectangle与Circle中的公共方法及属性被封能装在Shape中,利用继承关系消除了重复的拷贝。 2. 简单抽象中存在的问题 如何利用Shape指针访问Rectangle和Circle对象呢?这是技术上的问题,解决的方法是采用虚函数技术。 由于Shape描述的是一种抽象的概念,不应该生成对象,这是合理性的问题,解决的方法是采用抽象类技术 。

10.2 虚函数 面向对象设计中的另一个重要概念——多态性。

10.2.1 多态性、静态联编与动态联编 1. 多态性 强制多态和重载多态统称为专用多态,而参数多态和包含多态称为通用多态。 指程序中具有相似功能的不同函数用同一个名称来实现,进而可以使用相同的方式来调用这些具有不同功能的同名函数。 面向对象的多态性可以严格地分为四类:强制多态、重载多态、参数多态和包含多态。 强制多态和重载多态统称为专用多态,而参数多态和包含多态称为通用多态。

10.2.1 多态性、静态联编与动态联编(续) 强制多态是指编译器对类型的隐式转换。 重载多态就是指利用函数重载所实现的多态。 如计算表达式“3.0+2”时编译器将2由int类型转换为double类型。强制多态可以避免类型转换的麻烦,减少编译错误。 重载多态就是指利用函数重载所实现的多态。 参数多态与模板相关联。 包含多态是指具有继承关系的类族(层次)中定义于不同类中的同名成员函数的多态行为。 主要是通过虚函数来实现的。

10.2.1 多态性、静态联编与动态联编(续) 2. 静态联编与动态联编 从处理时刻看,多态可以划分为两类:编译时的多态和运行时的多态。 联编(或绑定):确定操作的具体对象的过程。 在编译连接阶段完成的联编称为静态联编(early binding)。也称为早期或先期联编。 强制多态、重载多态和参数多态都通过静态联编处理。 在程序运行阶段完成的联编称为动态联编(late binding),也称为晚期或后期联编。 包含多态是以动态联编方式处理的。

10.2.2 用虚函数实现动态联编 1. 虚函数的语法 虚函数(virtual function)是指一个类中定义的一类特殊方法,声明形式如下: virtual 函数原型; class Base { ... public: virtual void method(); //虚函数声明 }; void Base::method() //虚函数实现,无virtual关键字 }

10.2.2 用虚函数实现动态联编(续) 2. 虚函数的作用 可以使指向派生类对象的基类指针(或引用)在不经类型转换的情况下直接调用派生类的方法。 见下页示例。 虚函数仅对继承关系有效,即如果基类和派生类中各自定义了相同的虚函数,系统将在运行时才确定基类指针所指向对象的类型,进而决定应该调用哪个类的方法。

virtual void print(){ cout << "Base Class A.\n"; } }; { public: virtual void print(){ cout << "Base Class A.\n"; } }; class B: public A //派生类 virtual void print(){ cout << "Derived Class B.\n"; } int main( ) { A a; B b; A* ap = &a; //基类指针指向基类对象 ap->print(); //调用基类方法 ap = &b; //基类指针指向派生类对象 ap->print(); //调用派生类对象 A& ar = b; //基类引用绑定到派生类对象 ar.print(); //调用派生类方法 } 程序运行时的输出结果如下: Base Class A. Derived Claaa B.

10.2.2 用虚函数实现动态联编(续) 3. 有关虚函数的说明 ⑴虚函数应在基类中声明,其特性被自动传递给派生类,故派生类中的virtual修饰可有可无。 ⑵实现虚函数关系的前提是两类之间以公有方式派生。 ⑶派生类中只有原型与基类的虚函数完全相同时才是虚函数。 见下页示例。

#include <iostream> using namespace std; class A { public: virtual void func1(int){ cout<<"A func1.\n"; } virtual int func2(float){ cout << "A func2.\n"; } }; class B:public A void func1(int){ cout << "B func1.\n"; } virtual int func2(int){ cout << "B func2.\n"; } int main( ) { B b; A* ap = &b; ap->func1(0); //调用B类的方法 ap->func2(0); //调用A类的方法 } B func1. A func2.

10.2.2 用虚函数实现动态联编(续) ⑷只有类的非静态方法可以是虚函数,静态方法不能是虚函数 ⑸派生类中不能定义与基类的虚函数名字参数都相同而仅仅返回值不同的方法。 例外:如果基类虚函数返回基类型指针或引用,派生类虚函数返回派生类类型的指针或引用,此时被认为合理,具有虚函数关系并被滞后联编。 virtual A* func1(int); //基类A中定义了虚函数 B* func1(int); ⑹内联函数不能是虚函数。如果虚函数在类内部实现,自动被视为非内联的方法。

10.2.3 构造、析构与虚函数 在C++中,构造函数不能是虚函数,也尽量不要调用虚函数,但析构函数可以是虚函数,且在一个类含有虚函数时析构函数通常也定义为虚函数。

class A { public: virtual ~A() { cout<<"Base class A destroyed.\n"; } }; class B : public A { public: ~B() { cout<<"Derived class B destroyed.\n"; } int main( ) { A* ap = new A; delete ap; ap = new B; } Base class A destroyed. Derived class B destroyed.

*10.2.4 虚函数的内部实现机制 此部分内容自学。

10.2.5 重载、覆盖和隐藏 在一个子类从基类派生时,由于既包含继承来的方法,又可以自己重新定义方法,且基类中可能定义了虚函数。因此,需要仔细辨别C++在处理这些问题上的细微差别。

{ cout << "A func1().\n"; } virtual void func2() class A { public: void func1() { cout << "A func1().\n"; } virtual void func2() { cout << "A func2().\n"; } }; class B : public A { cout << "B func1().\n"; } void func1(int) { A::func1(); func1(); cout<<"B func1(int).\n"; } void func1(double) { cout << "B func1(double).\n"; } void func2() { cout << "B func2().\n"; } void func2(int) { cout << "B func2(int).\n"; } int main( ) { A oA,*pA; oA.func1(); //静态调用A类的func1 oA.func2(); //静态调用A类的func2 pA = &oA; pA->func1(); //静态调用A类的func1 pA->func2(); //动态调用A类的func2 B oB; oB.func1(); //静态调用B类的func1 oB.A::func1(); //静态调用A类的func1 oB.func1(1); //静态调用B类的func1(int) oB.func2(); //静态调用B类的func2 oB.func2(1); //静态调用B类的func2(int) pA = &oB; pA->func2(); //动态调用B类的func2 }

10.2.6 动态造型(dynamic_cast) dynamic_cast只用于指针(引用)类型转换,语法形式为: dynamic_cast<type*>(expression) type必须是有虚函数的类的指针、引用或者void *(空类型)指针 type是类的指针或引用时,expression也必须是指针或引用。 class A { ... }; class B : public A { ... }; void func(A *pa) { A a; B b; //两个对象 A* pa = &a; //基类指针指向基类对象 B* pb1 = dynamic_cast<B*>(pa); //指针pb1为0 pa = &b; //基类指针指向子类对象 B *pb2 = dynamic_cast<B*>(pa); //指针pb2指向对象b }

10.2.6 动态造型(dynamic_cast) (续) 在类层次间进行上行(子类向父类)转换时,dynamic_cast与static_cast的效果相同。 在进行下行(父类向子类)转换时,dynamic_cast具有类型检查功能,比static_cast更安全。 如果类型检查失败,表达式dynamic_cast<type>的值为0(空指针)或导致运行错误(对于引用)。

10.3 纯虚函数与抽象类 10.3.1 纯虚函数 对于没有函数体的虚函数方法,派生类重新定义原型相同的虚函数来覆盖它,以使得程序在处理指向派生类对象的基类指针和引用时能够实现动态联编。 对于此类方法,C++采用了一种特殊的方式,并将其定义为纯虚函数 。 纯虚函数的语法形式为: virtual type func_name(形式参数表) = 0;

10.3 纯虚函数与抽象类(续) 10.3.2 抽象类 如果一个类至少含有一个纯虚函数,那么就称其为抽象类。 C++对于抽象类的使用有如下限制: ⑴抽象类只能用作其它类的基类,不能建立抽象类对象。 ⑵抽象类不能用作参数类型、函数返回类型或参加显式转换类型。 ⑶可以声明指向抽象类的指针和引用,目的是使指针能指向它的派生类,进而实现多态性。 ⑷若一个类是某个抽象类的派生类,则需要重新定义基类的所有纯虚函数,才能成为具体类,否则仍是抽象类。 见例10_5.

最后说明: 虚拟继承与虚函数及多态性没有关系。