第五章多态
第5章继承与多态
两者的不同之处在于:子类隐 藏父类的属性只是使之不可见,父 类的同名属性在子类对象中仍然占 有自己的独立的内存空间; 而子类方法对父类同名方法的 覆盖将清除父类方法占用的内存, 从而使父类方法在子类对象中不复 存在。
cc
1 5.6 c x x x show1() show() 程序中类 B 继 承类 A,类 B 派生类 C,三个类有同名的 变量 x 和同名的方法 show 。 为 了 访 问 父 类中被覆盖的同名
6 class IP_Card extends Number_ PhoneCard { Date expireDate; boolean performDial ( ) { if ( balance > 0.3 && expireDate.after( new Date ( ) ) ) { balance - = 0.3; return true ; } IP卡 else 属性:失效日期 return false; } 方法:拔打电话 }
类体;
}
图5-1 电话卡的继承关系
继承
电话卡
属性:剩余金额
方法:拔打电话, 查询余额
继承
无卡号电话卡
属性:对应电话 机型号
有卡号电话卡
属性:卡号,密码, 拔入号码、接通 方法:登录交换机
继承 继承 继承
方法:获得电话 机型号
继承
电话磁卡
属性:使用地域 方法:拔打电话
电话IC卡
属性: 方法:拔打电话
3.
方法
非访问控制符:abstract, static, final, native, synchronized
表4-1 类、属性和方法的访问控制
类
public
c++第5章
27
【例5-3】 有一个交通工具类vehicle,将它作为基类派 生出汽车类motor_ vehicle ,再将汽车类motor_ 5.2 向上类型转换 vehicle 作为基类派生出小汽车类car和卡车类truck, 声明这些类并定义一个虚函数用来显示各类信息。程序 如下: #include <iostream> using namespace std; class vehicle //基类vehicle声明 {public: virtual void message() //虚成员函数 { cout<<"vehicle message "<<endl; } private: int wheels; //车轮个数 float weight; //车重 }; 28
5.2 向上类型转换
15
函数fun()接受一个B0类的对象,但也不拒 绝任何B0派生类的对象 在B0类中有的接口必然存在于B1类和D1类中, 因为B1类和D1类都是B0类的公用派生类
B1类和D1类到B0类的向上类型转换会使B1类 和D1类的接口“变窄”,但不会窄过B0类的 整个接口B1类和D1类
3次都是对B0::display()的调用,这不是我 们希望的输出 我们希望通过使用基类指针,不仅能够调用 基类对象的成员,而且能够调用派生类对象 的成员 运行结果 即我们希望的结果 这需要知道绑定这个概念
13
5.2 向上类型转换
•向上类型转换是指把一个派生类的对象 作为基类的对象来使用。 •向上转型中有三点需要我们特别注意。 •向上类型转换是安全的。 •向上类型转换可以自动完成。 •向上类型转换的过程中会丢失子类型 信息。
14
【例5-1】一个向上类型转换的例子。 #include <iostream> using namespace std; class B0 //基类B0声明 {public: void display(){cout<<"B0::display()"<<endl;} };//公有成员函数 class B1: public B0 {public: void display(){cout<<"B1::display()"<<endl;}}; class D1: public B1 {public: void display(){cout<<"D1::display()"<<endl;}}; void fun(B0 *ptr) { ptr->display(); } //通过“对象指针->成员名”的形式调用display()成员函数 int main() { B0 b0; //声明B0类对象 B1 b1; //声明B1类对象 D1 d1; //声明D1类对象 B0 *p; //声明B0类指针p 运行结果 p=&b0; //B0类指针p指向B0类对象 fun(p); B0::display() p=&b1; //B0类指针p指向B1类对象 fun(p); B0::display() p=&d1; //B0类指针p指向D1类对象 B0::display() fun(p); return 0;}
第五章多态性
{cout<<"thisistheclassofbase2!"<<endl;}
}; class derive:public base1, public base2 //定义派生类derive { public: void who() {cout<<"this is the class of derive!"<<endl;} };
5.2运通 运算符重载
友元 运算符重载
成员 运算符重载
5.2.1 普通运算符重载
运算符重载的定义: 类型名 operator@(参数表)
{
// 函数体
(要重载的运算符)
}
5.2.1 普通运算符重载
class complex { public: double real,imag; complex(double r=0,double i=0) { real=r; imag=i;} }; int main() { complex com1(1.1,2.2), com2(3.3,4.4), total; total=com1+com2; //错误 //… return 0; complex operator+(complex om1,complex om2) } { complex temp; temp.real=om1.real+om2.real; temp.imag=om1.imag+om2.imag; return temp; }
C++面向对象程序设计 LOGO
第五章 多态性
任课老师:刘 晶
电子邮箱 :liujing@
5.1 多态性概述
面向对象设计中多态性就是不同对象收到相同 的消息时,产生不同的动作。 C++中的多态性是指用一个名字定义不同的 函数,这些函数执行不同但又类似的操作,从 而可以使用相同的调用方式来调用这些具有不 同功能的同名函数。
第五章 多态
int main( ){ D*p; p=static_cast<D*>(new B); p->m( ); //error }
5.1.5 虚析构函数
如果基类有一个指向动态分配内存的数据成 员,并定义了负责释放这块内存的析构函数,就 并定义了负责释放这块内存的析构函数, 应该将这个析构函数声明为虚成员函数。 应该将这个析构函数声明为虚成员函数。
看例5-9 看例
5.1.6 对象成员函数和类成员函数
只有非静态成员函数才可以是虚成员函数。 只有非静态成员函数才可以是虚成员函数。 class C{ public: static virtual void f( );//error static void g( ); virtual void h( ); };
5.4.2 定义纯虚成员函数的规则
只有虚成员函数才可以成为纯虚成员函数, 只有虚成员函数才可以成为纯虚成员函数,非虚 函数或顶层函数都不能声明为纯虚成员函数。 函数或顶层函数都不能声明为纯虚成员函数。 void f( )=0; / /error class C{ public: void open( )=0; //error };
#include <iostream> using namespace std; void sayHi( ); int main( ) { sayHi( ); return 0; } } void sayHi( ) { cout<<“Hello,cruel world!”<<endl;
5.1.1 C++多态的前提条件
5.4 抽象基类
如果一个类中至少有一个纯虚函数, 如果一个类中至少有一个纯虚函数 , 那么这个类 被成为抽象类( 被成为抽象类(abstract class)。 )
多态知识点总结
多态知识点总结多态的定义多态是指在不同的对象上调用相同的方法时,根据对象的不同而执行不同的操作。
简而言之,多态就是同一个方法调用在不同的对象上有不同的行为。
多态的实现多态的实现依赖于以下两个重要的概念:继承和方法重写(覆盖)。
在继承关系中,子类可以继承父类的方法,并进行重写,实现不同的行为。
当调用父类的方法时,如果子类已经重写了这个方法,就会调用子类的方法,从而实现多态的效果。
例如,有一个动物类Animal,其中有一个方法speak(),然后有狗类Dog和猫类Cat都继承自Animal类,并且它们都重写了speak()方法,分别实现了不同的叫声。
当调用speak()方法时,根据对象的不同会执行不同的叫声,这就是多态的实现。
多态的特点多态的特点主要包括以下几点:1. 编译时类型和运行时类型不一致在多态的情况下,对象的编译时类型和运行时类型可能不一致,这就是多态的实现。
编译时类型是指在编译期间确定的对象类型,而运行时类型是指在运行时确定的对象类型。
2. 动态绑定在进行多态调用时,方法的实际调用取决于对象的运行时类型,这就是动态绑定。
动态绑定确保了程序在运行时能够调用正确的方法。
3. 提高程序灵活性和可扩展性多态可以通过创建抽象类和接口,来定义一组相关的操作,并让不同的子类去实现这些操作,从而提高了程序的灵活性和可扩展性。
多态的优点多态的优点主要包括以下几点:1. 提高了代码的可读性和可维护性多态使得代码更加清晰和简洁,可以更好地理解和维护代码。
2. 降低了耦合度多态可以减少代码之间的耦合,提高了代码的灵活性和可扩展性,使得系统更易于维护和扩展。
3. 增加了代码的复用性通过多态,可以将通用的操作抽象出来,写成接口或抽象类的形式,从而增加了代码的复用性。
多态的应用场景多态广泛应用于面向对象编程中的各个领域,尤其是在设计模式和软件开发中,常常会用到多态来提高代码的灵活性和扩展性。
1. 接口和抽象类在接口和抽象类中经常用到多态,通过定义抽象方法来实现多态,从而让不同的子类去实现不同的行为。
C++程序设计 第五章
(1) 一般来讲,重载的功能应当与原有功能类似。 (2)只能重载原先已有定义的运算符。 (3)运算符“.”、“*”、“::”、sizeof、“?:”不能重 载。 (4)不能改变运算符的操作个数。 (5)不能改变运算符原有的优先级。 (6)不能改变运算符原有的结合特性。 (7)不能改变运算符对预定义类型数据的操作方式。
5.4 类型转换
在过去的学习中,我们会经常遇到一种问题-类型转换。 在基本数据类型中,我们用到过自动类型转换 (隐式)和强制类型转换(显式)。 今天着重介绍我们自己定义的类之间如何转换。
5.4.1 基本数据类型的转换
1、隐式转换(自动转换)规则:
A=B 数据类型自动提升。(char->int/short->int) 低级别自动转换为高级别。
习惯形 式
友元运算符函数调用形式
成员运算符函数调用形 式
a+b -a a++ ++a
operator+(a,b)
a.operator+(b)
operator-(a)
operator++(a,0) operator++(a)
a.operator-()
a.operator++(0) a.operator++()
5.3.2 友元运算符函数
上例中我们不管用那种方式重载+运算符,都只能访问对象的公有成员 (其中私有成员是通过公有的get、set方法来访问),如果想要直接访 问私有成员就要用以下两种方式:
定义为它将要操作的类的成员函数 定义为类的友元函数
Java第5章继承与多态1
class A { int x = 1234; void show( ) { System.out.println("class A : "); } } class B extends A { double x = 567.89; //数据成员覆盖 不提倡这样使用。 数据成员覆盖, 数据成员覆盖 不提倡这样使用。 void show( ) //成员方法覆盖 成员方法覆盖 { super.show( ); System.out.println("class B : "); } }
学生 目录
选修 5~200 存放 1 * *
课程 文件
程序中, 在Java程序中,常用成员对象实现。 程序中 常用成员对象实现。
聚合关系
顾名思义, 顾名思义,聚合是将多个类聚集在一起 的意思。 聚合的方式有如下几种: 的意思。 聚合的方式有如下几种: 包含关系( 包含关系(has a) ) 组成关系( 组成关系(part of) ) 所属关系( 所属关系(owns) ) 雇佣关系( 雇佣关系(employed_by) )
Point -int x,y +setPoint() +getX() +getY() +String to String() Square
第3次上机作业 次上机作业
+getEdge() +setEdge()
四、多态性的实现
类对象之间的类型转换
ObjectType Object1=new ObjectType( ); ObjectType Object2=Object1;
《注意》 注意》
没有extends,默认父类为Object ,默认父类为 没有 只能有一个父类, 只能有一个父类,即单继承 子类继承父类中除private部分的全部内容 部分的全部内容 子类继承父类中除 类继承具有传递性
第五章 继承与多态
Teacher
Student
Cloneable()接口
• • • • • • • • • • • • • class Circle { public Point point=new Point(1,2); public int[]arrayInt=new int[]{1,2,3,4,5,6}; public double radius=50; public Circle(Point p,int[] array,double radius){ public Circle clone(){}//需要实现 } public class CircleTest { public static void main(String[] args) { Circle c=new Circle(); Circle d=c.clone(); } }
பைடு நூலகம்含的语句
方法的覆写
例子
子类想调用父类被覆写的方法
内部类
• 在类内部也可以定义另一个类。如果类Outer的内部再定 义一个类Inner,则Inner被称为内部类。 • 内部类可声明public和private,对它的访问的限制与 成员方法和变量相同。 • Public class Outer{ • //外部类的成员 • public class Inner{ • //内部类的成员 • } • }
interface Network{ public void browse() ; // 浏览 } class Real implements Network{ public void browse(){ System.out.println("上网浏览信息") ; } }; class Proxy implements Network{ private Network network ; // 代理对象 public Proxy(Network network){ work = network ; } public void check(){ System.out.println("检查用户是否合法。 ") ; } public void browse(){ this.check() ; work.browse() ; // 调用真实的主 题操作 } }; public class ProxyDemo{ public static void main(String args[]){ Network net = null ; net = new Proxy(new Real()) ;// 指定代 理操作 net.browse() ; // 客户只关心上 网浏览一个操作 } };
第5章 继承与多态-2
【课堂示例(三)】 课堂示例(
P119:例5-9 多态测试。
CASE3:eg5_9.java
【注意】 注意】 对象类型转换的方法。 对象类型转换的方法。
f(Employee emp){ if(emp instanceof Manager){ Manager mgr= (Manager)emp; …} else if (Emp instanceof Diredtor){ Director dir=(Director)emp;{…} else {…} } }
5.3 多态
5.3.2 方法覆盖实现的多态
子类中定义与父类中同名,并且参数类型和个数也相同的方法, 被称为方法的覆盖(override)。这些方法的定义后,在子类的 实例化对象中,父类中继承的这些同名方法将被隐藏。 覆盖方法的区分:只需在调用方法时指明调用的是哪个类的方法。
5.3 多态
5.3.3 方法重载实现的多态
本节内容
5.3 多态
5.3.1 5.3.2 5.3.3 5.3.4 多态的概念 方法覆盖实现的多态 方法重载实现的多态 对象引用的多态
5.3 多态
5.3.1 多态的概念
多态是面向对象程序设计的又一个特殊特性。利用面向过程的语 言编程,主要工作是编写一个个过程或函数。这些过程和函数各 自对应一定的功能,它们之间是不能重名的,否则在用名字调用 时,就会产生歧异和错误。而在面向对象的程序设计中,有时却 需要利用这样的“重名”现象来提高程序的抽象度和简洁性。 所谓多态,是指一个程序中同名的不同方法共存的情况。
class A508{ public static void main(String[ ] args){ F508 f = new F508(); f.fun(2,5); S508 s=new S508(); s.fun(2,5); s.fun(2.2,5); f =s; f.fun(2,5);//f.fun(2.2,5); } }
C++ 第五章 多态
例4、为复数类重载一元和二元减运算符//区分? complex complex:: operator-(complex& c) { double r=real-c.real, i=imag-c.imag; return complex(r,i);} complex complex:: operator-() { return complex(-real,-imag);}
④重载运算符函数的返回类型可以任意,但应当结合 语义返回一个恰当的类型,通常与参数类型一致。 ——构造复杂的表达式
5.2.2 友元运算符重载函数
1、定义形式 类内: friend 返回类型 operator θ(参数) { ……. } 类外: 类内声明 friend 返回类型 operator θ(参数); 类外实现 返回类型 operator θ(参数) { ……. }
调用形式:显式调用,隐式调用 成员函数: c3=c1.operator+(c2); c3=c1+c2; 友元函数: c3=operator+(c1,c2); c3=c1+c2 推荐
重载要求: ①程序员不能增加新的运算符,只能从已有的运算 符集中选择一个合适的运算符进行重载。
可重载的运算符有41个:
不可重载的运算符有: . .* :: ?: sizeof #
②重载后的运算符优先级、结合性和使用语法不变, 运算语义可以不同(尽量类似)。 典型例:<< >> ③一元二元操作符的参数个数必须不变,且不能重载 为带缺省参数,以避免意义不清。当重载为成员函数 时一般由当前对象充当一个参数,而重载为友元函数 时参数都要显式给出。
算术运算符7个 + - * / % ++ - 位操作符6个 ~ & | ^ << >> 逻辑操作符3个 && || ! 关系运算符6个 > < = = != >= <= 赋值操作符11个 = += -= *= /= %= ^= &= |= <<= >>= 括号2组()[] 逗号运算符, 指针操作-> 堆操作 new delete new[] delete[]
09_第5章 继承和多态
卡是不适合的。从有卡号电话卡到IP电话卡和200电话卡的继承遵循完
全相同的原则。使用继承的主要优点,是使得程序结构清晰,降低编 码和维护的工作量。
单重继承:是指任何一个类都只有一个单一的父类。
多重继承:是指一个类可以有一个以上的父类,它的静态
的数据属性和操作从所有这些父类中继承。
采用单重继承的程序结构比较简单,是单纯的树状结构,掌 握、控制起来相对容易;而支持多重继承的程序,其结构则 是复杂的网状,设计、实现都比较复杂。 Java语言仅支持类的单重继承。
(2)定义子类。Java中的继承是通过extends关键字来实 现的,在定义类时一使用extends关键字指明新定义类的 父类,就在两个类之间建立了继承关系。具体语法是:
[类修饰符] class子类名 extends 父类名
通过该extends关键字来指明子类所要继承哪一 个父类。如果,父类和子类不在同一个包中, 则需要使用import语句来引入父类所在的包。 (3)实现子类的功能。子类具体要实现的功能 由类体中相应的域和方法来实现,其编写和一
般的类是完全相同的。
例 5.1 继承的实现 class A{ public int a1; private float a2 int getA(){ return(a1); } void setA(){} } class B extends A { int b1; String b2; String getB() { return(b2); } 可见父类的所有非私有成员实际是各子类所拥有 } 集合的一部分。子类从父类继承成员而不是把父 class C extends B { 类的数据成员复制一遍,这样做的好处是减少程 int c; 序维护的土作量。从父类继承来的成员,就成为 了子类所有成员的一部分,子类可以使用它。 int printC(){ System.out.println(c);}}
第5章 继承与多态
课本P104
-18-
抽象类
• 抽象类
抽象类和接口两种机 制的引入,使Java拥有 制的引入,使Java拥有 了强大的面向对象编 程能力。
在面向对象的概念中,所有的对象都是通过类来表述,但并 不是所有的类都是用来描绘对象的,如果一个类中没有包含 足够的信息来描绘一类具体的对象,这样的类就是抽象类。 抽象类的作用类似“模板”,其目的是要设计者按照它的格 式来修改并创建新的类,但是并不能直接由抽象类创建对象, 只能通过抽象类派生出新的类,再由新类来创建对象
-9-
多态
• 重写
当一个子类继承了一个父类时,可以在子类中直接使用 父类的属性和方法。如果父类的方法无法满足子类的需求, 则可以在子类中对父类的方法进行改造,也称作重写 (override)。重写是Java多态性的另一种体现。
• 重写的原则
重写的方法的签名必须要和被重写的方法的签名完全匹配; 重写的方法的签名必须要和被重写的方法的签名完全匹配; 重写的方法的返回值必须和被重写的方法的返回一致或者是其子类; 重写的方法的返回值必须和被重写的方法的返回一致或者是其子类; 重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致, 重写的方法所抛出的异常必须和被重写方法的所抛出的异常一致,或者 是其子类; 是其子类; 私有方法不能被重写 子类重写父类方法的过程中,可访问性只能一样或变的更公开。 子类重写父类方法的过程中,可访问性只能一样或变的更公开。
• 聚合(has-a) has-a
聚合表现的则是类A包含类B的关系,如:一个Car类的对象包含一个 Motor类型的属性
• 继承(is-a)
加自己的特性
继承是面向对象编程的 继承表现的是一种共性与特性的关系,如果类B和类C继承自类A,那 一项核心技术,是面向 对象编程技术的一块基 么类A规定了类B和类C的共性,类B和类C在继承类A的基础上可以添 石
第5章 继承和多态
学习导读
本章讨论面向对象的编程(OOP)及其关键技
术:继承和多态。 继承性是软件复用的一种形式,对降低软件复 杂性行之有效。继承性同时是面向对象程序设计 语言的特点,采用对象但没有继承性的语言是基 于对象的语言,但不是面向对象的语言,这是两 者的区别。 多态性允许以统一的风格处理已存在的变量及 相关的类,使增加系统中新功能变得容易。
Manager
5.1 继承
过程:e. getSalary()
•程序会选择正确的getSalary方法。注意尽管e的声明类型 是Employee.
•当 e 指 向 一 个 Employee 对 象 时 , e.getSalary() 会 调 用 Employee类中的getSalary方法;而当e指向一个Manager对 象时,getSalary()方法就变成了Manager类的getSalary() 方法。虚拟机知道e所指对象的实际类型,因此它会调用正 确的方法。
例5.8 接口的示例。 //Movable.java interface Movable{ //定义接口Movable public abstract void move(); } //Drawable.java interface Drawable{ //定义接口Drawable public abstract void draw(); } //Shapable.java interface Shapable extends Movable,Drawable{ //继承了Movable和 Drawable接口; public abstract void run(); } 在例5.8中定义了三个接口:Movable、Drawable和Shapable,接口Shapable 继承了接口Movable和接口Drawable,这意味着接口Shapable继承接口 Movable的抽象方法move()和继承了接口Drawable的抽象方法draw ()。
第5章 多态
1高质量C++编程第5章多态本章包含如下内容:5.1 虚函数概念5.2 虚函数的实现机制5.3 相关继承和多态的深入探讨时间:6 学时教学方法:PPT讲解2•多态性(polymorphism)是面向对象程序设计的一个重要特征,利用多态性可以设计和实现一个易于扩展的系统。
•在C++程序设计中,多态性是指具有不同功能的函数可以用同一个函数名,这样就可以用一个函数名调用不同内容的函数。
•在面向对象方法中一般是这样表述多态性的: 向不同的对象发送同一个消息,不同的对象在接收时会产生不同的行为(即方法)。
也就是说,每个对象可以用自己的方式去响应共同的消息。
34强制多态参数多态重载多态包含多态多态通用多态专用多态虚函数机制(重点)const_cast const_cast< >< >static_cast static_cast< >< >reinterpret_cast reinterpret_cast< >< >dynamic_cast dynamic_cast< >< >C 方式的()函数模板函数重载类模板运算符重载(难点)5.1.1 多态基础-三个基本概念•面向对象编程基于三个基本概念:封装、继承和动态绑定•在 C++ 中:– 1. 用类进行数据抽象和封装– 2. 继承:保持已有类的特性而构造新类的过程。
– 3. 动态绑定使编译器能够在运行时决定是使用基类中定义的函数还是派生类中定义的函数。
55.1.1 多态基础-继承和动态绑定的作用•继承和动态绑定在两个方面简化了我们的程序:–能够容易地定义与其他类相似但又不相同的新类–能够更容易地编写忽略这些相似类型之间区别的程序。
•现在写的程序可以调用将来(按规定)写的函数。
无论再从Shape派生出什么类型,希望这段代码都能够适用.float GetArea(const Shape& shape){return shape.Area();// shape为何我们不想关心!}6•从实际问题出发•我们的Clock必须要有说明性文字class Clock{public:void ShowInstruction(){cout<<"能显示时间的表,售价150元!"<<endl;}private:int m_Hour;int m_Minute;};7•ShowInstruction在派生类中的实现需要改变class Media_Clock : public Clock{public:void ShowInstruction(){cout<<"能播放音乐的表,售价200元!"<<endl;}};89•对象快疯了,我到底是谁?!•能显示时间的表,售价¥150。
多态 PPT课件
面向对象的类库的体系结构
class B { public: virtual void OnDraw() { cout<<"B:m()"<<endl; } void Run() { if( getMsg()==WM_PAINT) OnDraw(); //….. } };
class D : public B { public: virtual void OnDraw() { cout<<"D:m()"<<endl; } }; D theApp; int main(int argc, char* rgv[]) { theApp.Run(); }
1)顶层函数 2)在一个类中 3)基类与派生类中 4)不同的基类中 5)不同的派生类中
5.3.1重载
函数同名,但具有不同的函数签名。 5.3.3 遮蔽 派生类中的函数,遮蔽基类的同名非虚 函数,即使是不同签名。(即基类派生类 间的重载’失效’.) 5.3.2 覆盖(override ) 派生类中的函数,覆盖基类的相同签名 虚函数,动态绑定,多态性。
编程人员不必了解虚成员函数的运行机制. 其伪解释: struct Vtbl { void (*vm)() };
void Func( BC *p) { p->vtbl.vm(); p->m(); }
b &BC:vm vtbl d &DC:vm
内存映射:
b
vtbl
多态性的运行开销比较: 虚表占用的内存,访问虚表.但显然开销大不了多少.
第5章 多 态
面向对象程序设计的基本特征之一:多 态
什么是多态?
字面意思:多种形态
例:铅笔移动,杯子移动,房子移动 移动操作(函数)具有多态性 C++含义:多态性是针对函数来说的,函数的执行是在运行 时才动态 绑定到函数的入口地址。 p对象->移动( ); 其p对象是基类型,运行时侦测实际类型确定调用函数(运行 时才知道)! 如果 对象是铅笔: p对象->铅笔::移动( ); 如果 对象是杯子: p对象->杯子::移动( ); 其移动( )函数具有多态性,移动( )函数是虚函数。 面向对象的多态性不但使程序结构更接近现实世界,且使开发 可互操作的组件成为可能. Ex5_1 实现的语法规则:
第5章+多态
编号: NO0001 编号: NO0001 编号: NO0001
周工资: 128 周工资: 128 周工资: 128
5.2.1 虚函数的意义
1、什么是虚函数
用virtual关键字修饰的成员函数
Virtual关键字其实质是告知编译系统,被指定为virtual 的函数采用动态联编的形式编译。
2、虚函数的定义形式
string name;
string Id;
};
class Manager:public Employee{
public: Manager(string Name,string id,float s=0.0):Employee(Name,id){ WeeklySalary=s;
} void setSalary(float s) { WeeklySalary=s; } //设置经理的周薪
}
本程序的运行结果如下:第1行输出没有体现虚函数特征 B::f D::f D::f
5.2.2 虚函数的特性
3.派生类中的虚函数要保持其虚特征,必须与基类 虚函数的函数原型完全相同,否则就是普通的重 载函数,与基类的虚函数无关。
5.2.2 虚函数的特性
4. 派生类通过从基类继承的成员函数调用虚函数时, 将访问到派生类中的版本。
void g(){ cout << "D::g"; } }; void main(){
D d; d.f(); }
5.3虚析构函数
基类析构函数几乎总是为虚拟析构函数,因 为:
假定使用delete和一个指向派生类的基类 指针来销毁派生类对象,如果基类析构函 数不为虚,就如一个普通成员函数,delete 函数调用的就是基类析构函数。在通过基 类对象的引用或指针调用派生类对象时, 将致使对象析构不彻底!
2010-第5~6讲---第五章 继承和多态
派生类继承
基类的所有成员 Automobile
增加成员进行 功能扩展
4
北京信息科技大学信息管理学院
4
public class Bus : Automobile public class Automobile { { private int passengers; protected float speed; public int Passengers public float Speed { { get { return passengers; } get { return speed; } set { passengers = value; } set { speed = value; } } } public Bus() private float weight; { public float Weight passengers = 20; { speed = 60; get { return weight; } Weight = 10; set { weight = value; } } } public float Run(float distance) } { return distance / speed; } } class Program { public static void Main() { Bus b1 = new Bus(); Console.WriteLine("客车行驶1000公里需{0}小时", b1.Run(1000)); } 5 北京信息科技大学信息管理学院 }
…
Delegate类 自定义枚举 EventHandler …
3
北京信息科技大学信息管理学院
3
5.1 继承
基类和派生类
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
操作符,必须包含头文件typeinfo。
typeid(typename) 或 typeid(expression) 例如: float x ; typeid(x)==typeid(float) typeid(x)==typeid(double) true flase
Tinker t1; t1.sayHi( );}
5.1.3 运行期绑定和虚成员函数表
class B{ public: virtual void m1( ){ } virtual void m2( ){ } }; 虚成员函数 B::m1 B::m2 入口地址示例 0x7723 0x23b4 class D:public B{ public:virtual void m1( ){ } };
class D:public B{
public:
virtual void m( ){
cout<<"hello"<<endl };
}
5.5.1 dynamic_cast操作符
在C++中,编译期合法的类型转换操作可能会在运行期引 发错误,当转型操作涉及到对象指针或引用时,更易发生错
误。
使用dynamic_cast操作符可用来在运行期对可疑的转型操 作进行测试。
public:
virtual C( ); virtual C(int ); virtual ~C( ); virtual void m( ); //error //error
}
5.1.5 虚析构函数
如果基类有一个指向动态分配内存的数据成员,并定义了 负责释放这块内存的析构函数,就应该将这个析构函数声 明为虚成员函数。 看例5-9
5.1.2 虚成员函数继承
基类中的虚成员函数也可被派生类继承。
class TradesPerson{ public: virtual void sayHi( ){cout<<“Just hi.”<<endl;} }; class Tinker:public TradesPerson { };
int main( ){
5.4.1 抽象基类和纯虚成员函数
纯虚成员函数:在虚成员函数声明的结尾加上=0。 抽象基类不能被实例化,而且必须至少有一个纯虚成员 函数。 class ABC{ public: virtual void open()=0; }; ABC obj;//error;
5.4.1 抽象基类和纯虚成员函数
抽象基类的派生类必须覆盖基类的所有纯虚成员函数,才 能用来创建对象。 class ABC{ public: virtual void open( )=0;};
class X:public ABC{
public :virtual void open( ){ } };
class Y:public ABC{ };
5.5 运行期类型识别
运行期类型识别(RTTI)提供如下功能:
在运行期对类型转换操作进行检查 在运行期确定对象的类型 扩展C++提供的RTTI
5.5.1 dynamic_cast操作符
一个基类指针不经过明确的转型操作,就能指向基类对象 或派生类对象;反过来,一个派生类指针指向基类对象是
虚成员函数 D::m1 D::m2
入口地址示例 0x99a7 0x23b4
5.1.3 运行期绑定和虚成员函数表
注意:使用动态绑定的程序会影响效率,因为虚成员 函数表需要额外的存储空间,而且对虚成员函数表进行查
询也需要额外的时间。
5.1.4 构造函数与析构函数
构造函数不能是虚成员函数,但析构函数可以是虚成员函数。 class C{
P TradesPerson sayHi( )
Tinker
Tailor
sayHi( )
sayHi( )
5.1.1 C++多态的前提条件
注意:
声明基类的一个成员函数为虚函数后,派生类没必要在 class C 派生类中显式的声明该成员函数为虚函数。但该函数在基 { virtual void f( ); //error 类和在派生类中必须具有相同函数签名。 public: int main( ) virtual void m( ); 如果函数在类声明之外定义,关键字virtual仅在函数声明 }; { 时需要。 void C::m( ) } { ...... C++仅允许将成员函数定义为虚函数,全局函数不能为虚 函数。 }
}
#include <string> 5.1 运行期绑定与编译期绑定 #include <iostream> using namespace std;
int main() 运行期绑定:在编译阶段不决定函数的入口地址,只有
class A { 在运行时才绑定函数的入口地址。 { public: virtual void sayHi(){cout<<"A::Hi"<<endl;} }; class B:public A { public : void sayHi(){cout<<"B::Hi"<<endl;} };
5.5.3 dynamic_cast小结
static_cast可施加于任何类型 dynamic_cast只能施加于具有多态性的类型,转型的目的 类型必须是指针或引用。 dynamic_cast能实施运行期类型检查。
5.5.4 typeid操作符
操作符typeid用来确定某个表达式的类型,要使用这个
5.4 抽象基类
在有些情况下,需要语言提供派生类必须覆盖某些虚函数 的保证机制。抽象基类就保证了这种机制; 公共接口:一个成员函数的集合,任何支持该接口的类必 须定义该集合中的所有函数; 如果一个类中至少有一个纯虚函数,那么这个类被成为 抽象类; 抽象类有一个重要特点,即抽象类必须用作派生其他类 的基类,而不能用于直接创建对象实例。
一种不明智的做法。
class B{ }; class D:public B{ }; int main( ){ D*p; p=new B;//error p=static_cast<D*>(new B);}
class B{ 使用static_cast进行转型操作是合法的,但可能会造成 public: 难以跟踪的运行期错误。 virtual void f( ){cout<< "Hi"<<endl;} }; int main( ){ D*p; p=static_cast<D*>(new B); p->m( ); //error
多态类型:至少有一个虚函数的类 int main( )
{ T*p=dynamic_cast<T*>(new C); //error return 0;
}
5.5.1 dynamic_cast操作符
注意:
dynamic_cast操作正确的前提是转型的源类型必须是多态
的, 但与转型的目标类型是否多态无关。 在< >中指定的dynamic_cast的目的类型必须是一个指针
C++中的编辑,编译,连接和运行
编辑
编译
连接
运行
建立、修改 源程序并把 它输入到计 算机
编译前进行 预处理 主要进行词 法分析和语 法分析 生成目标代 码文件.obj
连接程序自 动将目标代 码与库文件 进行连接处 理 生成可执行 文.exe
运行一个经 过编译、连 接后生成的 可执行文件
A *a=new B;
a->sayHi(); delete a; return 1;
}
5.1.1 C++多态的前提条件
C++中多态的三个前提条件:
必须存在一个继承体系结构 类必须具有相同函数签名的virtual成员函数。
至少有一个基类类型的指针或基类类型的引用。
5.1.1 C++多态的前提条件
或引用。
看一个正确的用法5-27
运行期类型安全检查演示程序
Book
title
Textbook level
PulpFiction
5.5.2 dynamic_cast的规则
从派生类到基类的dynamic_cast可以进行,这称为向上转型
从基类到派生类的dynamic_cast不能进行,称为向下转型。 有继承关系,派生类可通过dynamic_cast向基类转换 没有继承关系,不能通过dynamic_cast互换
5.5.1 dynamic_cast操作符
C++提供的dynamic_cast操作符可以在运行期检测某个 转型动作是否安全。dynamic_cast和static_cast有同样的语 class C{ };//no virtual methods 法,不过dynamic_cast仅对多态类型有效。 class T{ };
第五章 多态
主要内容
• • • • 运行期绑定与编译期绑定 重载、覆盖和遮蔽 抽象基类 运行期类型识别
引言
多态性是面向对象程序设计的重要特征之一。 多态性是指发出同样的消息被不同类型的对象接 收时导致完全不同的行为。 多态性是运行期绑定机制,绑定机制实现将函数
名动态地绑定到函数入口地址。
函数的入口地址:一个函数在内存中的起始地址
5.3.2 覆盖
假定基类B有一个成员函数m,其派生类D也有一个具有相 同函数签名的成员函数m,如果这个成员函数是虚函数,我们
称派生类的成员函数D::m覆盖了其基类的成员函数B::m。
如果成员函数不是虚函数,那么任何调用均为编译期绑定。