Presentation is loading. Please wait.

Presentation is loading. Please wait.

Object-Oriented Programming: Polymorphism

Similar presentations


Presentation on theme: "Object-Oriented Programming: Polymorphism"— Presentation transcript:

1 Object-Oriented Programming: Polymorphism
Chapter 13 Object-Oriented Programming: Polymorphism

2 OBJECTIVES What polymorphism(多态) is, how it makes programming more convenient, and how it makes systems more extensible and maintainable. To declare and use virtual functions(虚函数) to effect polymorphism. The distinction between abstract and concrete classes(抽象类和具体类). To declare pure virtual functions(纯虚函数) to create abstract classes.

3 Topics 13.1 Introduction 13.2 Relationships Among Objects in an Inheritance Hierarchy 13.3 Type Fields and switch Statements 13.4 Abstract Classes and Pure virtual Functions 13.5 Case Study: Payroll System Using Polymorphism

4 13.1 Introduction ---通用化编程需求
2018年12月8日3时58分 13.1 Introduction 通用化编程需求 画板 Tool 这里保存指针是否是保存派生类的指针?这里是否暗指需要virtual函数? 基类Shape:长方形*2、椭圆形*3、三角形*4、菱形*5等 用vector或者array来保存指针 基类指针 vs 派生类指针 当需更新画板时,枚举指针并调用各自draw函数 当需计算面积时,枚举指针并调用各自area函数

5 13.1 Introduction --- 需求 用户通过键盘输入多个员工信息,统计收入数据:
2018年12月8日3时58分 13.1 Introduction 需求 用户通过键盘输入多个员工信息,统计收入数据: (1)CommissionEmployee name, ssn, grossSales, commisionRate (2)BasePlusCommissionEmployee name, ssn, grossSales, commisionRate, baseSalary 用vector或者array来保存指向员工对象的指针 CommissionEmployee Pointer 希望通过这些通用指针来调用各自的earnings()函数以进行统计

6 13.1 Introduction 面向对象 数据封装 继承 多态

7 13.1 Introduction 多态: C++语言支持两种类型的多态:
2018年12月8日3时58分 13.1 Introduction 多态: 解释1:同样的消息被类的不同对象接收时导致的完全不同的行为的一种现象。这里所说的消息即对类的成员函数的调用。 解释2:通过指向派生类的基类指针,调用派生类的函数; 将不同的派生类对象都当作基类来处理,并根据对象不同产生不同的行为,以屏蔽各派生类对象之间的差异。写出通用的代码,使得程序员可以方便地处理普遍性问题。 C++语言支持两种类型的多态: 编译时的多态(静态多态) ——函数重载 运行时的多态(动态多态) ——虚函数 多态性提高了软件的可扩展性,使得可以用与接收消息对象类型无关的方式编写 函数重载和虚函数间的关系

8 Topics 13.1 Introduction 13.2 Relationships Among Objects in an Inheritance Hierarchy 13.3 Type Fields and switch Statements 13.4 Abstract Classes and Pure virtual Functions 13.5 Case Study: Payroll System Using Polymorphism

9 13.2 Relationships Among Objects in an Inheritance Hierarchy
Invoking Base-Class Functions from Derived-Class Objects(基类指针指向派生类,调用基类函数) Aiming Derived-Class Pointers at Base-Class Objects(派生类指针指向基类,错误) Derived-Class Member-Function Calls via Base-Class Pointers(基类指针指向派生类,调用派生类函数,错误) Virtual Functions(应用虚函数,解决上述问题) Summary of the Allowed Assignments Between Base-Class and Derived-Class Objects and Pointers(基类/派生类对象和指针之间的赋值)

10 13.2.1 Invoking Base-Class Functions from Derived-Class Objects
基类CommissionEmployee void print() const; 派生类BasePlusCommissionEmployee commissionEmployeePtr = &basePlusCommissionEmployee; 注意:基类指针调用函数,输出什么 P 程序解读

11 Example class B 继承 class A, 即 B is a A 正确 不调用强制类型转换 class A class B x
B big; A small = big; A &refSmall = big; A *pSmall = &big; 正确 不调用强制类型转换 reinterpret_cast 不加强制类型转换会怎样? class A class B x x y y 基类部分 z 派生部分

12 13.2.1 Invoking Base-Class Functions from Derived-Class Objects
通过指向派生类的基类指针,调用的是基类的函数 结论: 调用基类还是派生类的函数,取决于句柄的类型,而不是句柄指向的实际对象类型

13 13.2.2 Aiming Derived-Class Pointers at Base-Class Objects
2018年12月8日3时58分 Aiming Derived-Class Pointers at Base-Class Objects 派生类指针指向基类对象 Compilation Error 第二句什么意思? P 13.6 程序解读

14 Example class B 继承 class A, 即 B is a A 正确 错误 考虑会产生何种后果? class A
B big; A small = big; A &refSmall = big; A *pSmall = &big; 正确 big = small; B *pBig = &small; 错误 考虑会产生何种后果? reinterpret_cast 不加强制类型转换会怎样? class A class B x x y y 基类部分 z 派生部分

15 13.2.3 Derived-Class Member-Function Calls via Base-Class Pointers
通过指向派生类的基类指针,调用的是基类的函数; 可否调用派生类独有的函数? Compilation Error 结论: 通过对象句柄,仅能调用该句柄类型的成员函数 P 13.7 程序解读

16 13.2.3 Derived-Class Member-Function Calls via Base-Class Pointers
解决办法: downcasting If the address of a derived-class object has been assigned to a pointer of one of its direct or indirect base classes, it is acceptable to cast that base-class pointer back to a pointer of the derived-class type.

17 B &refBig = (B &) refSmall;
Example class B 继承 class A, 即 B is a A B big; A small = big; A &refSmall = big; A *pSmall = &big; 正确 big = small; B *pBig = &small; 错误 B &refBig = (B &) refSmall; B *pBig = (B *) pSmall; × B *pBig = (B *) (&small); reinterpret_cast 为什么不能用Static_cast? class A class B x x y y 基类部分 z 派生部分

18 Virtual Functions With virtual functions, the type of the object being pointed to, not the type of the handle, determines which version of a virtual function to invoke. 虚函数: 调用哪个(基类/派生类)虚函数,由对象类型而不是句柄类型决定.

19 13.2.4 Virtual Functions --- 语法
基 类 class Shape{ public: virtual void draw() const; }; 派生类 class Rectangle : public Shape{ 可省略。只要基类声明函数为虚函数,则所有派生类的该函数均为虚函数

20 13.2.4 Virtual Functions --- 语法
基 类 class Shape{ public: virtual void draw() const; }; 派生类 class Rectangle : public Shape{ Override(覆盖) From an implementation perspective, overriding a function is no different than redefining one.

21 13.2.4 Virtual Functions 虚函数用于继承结构中的基类和派生类,以实现多态.
2018年12月8日3时58分 Virtual Functions 虚函数用于继承结构中的基类和派生类,以实现多态. 派生类中覆盖(Overridden)的虚函数和基类中的虚函数必须函数签名和返回值均相同. 包括函数名称、返回值、参数个数、类型、是否const都要一致 Rectangle rect; Shape *p = ▭ p->draw();

22 13.2.4 Virtual Functions 程序解读 P 13.8-10 调用虚函数的两种方式:
通过指向派生类的基类指针(或引用)调用,程序会在执行时(execution time)根据对象类型动态选择合适的派生类函数 – 动态绑定( dynamic binding )或延迟绑定( late binding ). 通过对象名和点操作符调用,程序在编译时(compile time)即根据对象类型确定函数– 静态绑定( static binding ). P 程序解读

23 13.2.4 Virtual Functions --- 若干限制
2018年12月8日3时58分 Virtual Functions --- 若干限制 只有类成员才能声明为虚函数 静态成员函数不能是虚函数 构造函数不能是虚函数 析构函数可以是虚函数,并且通常声明为虚函数(注意基类和派生类的析构函数不同名) 例: commissionEmployeePtr = &basePlusCommissionEmployee delete commissionEmployeePtr; 调用的是basePlusCommissionEmployee的析构函数 虚函数是为了多态设计的....静态成员函数独立于对象存在,没有this指针...所以不能设计成虚函数...

24 derived-class pointer
2018年12月8日3时58分 Summary of the Allowed Assignments Between Base-Class and Derived-Class Objects and Pointers base-class pointer derived-class pointer base-class object OK ERROR derived-class object 只有通过引用或指针来访问对象的虚函数时才进行动态绑定。 通过引用或指针访问对象的非虚成员函数,采用静态绑定。(与句柄类型的成员函数代码绑定) 通过“类名+::”访问对象成员函数,也采用静态绑定。 Employee::Print()

25 基类函数中调用虚函数时 Base! Derived! class Base { public: Base() {}
virtual ~Base() {} virtual void print(void) { cout << "Base! "; } void get(void ) { print(); }; class derived : public Base { public: derived() {} ~derived() {} virtual void print(void) { cout << "Derived! "; } }; int main() { Base a, *ptr; derived b; a.get(); ptr = &b; ptr->get(); return 0; } Base! Derived!

26 { cout<<"1"<<endl; } virtual void fuu()
2018年12月8日3时58分 class A { public: void foo() { cout<<"1"<<endl; } virtual void fuu() { cout<<"2"<<endl; } }; class B: public A { cout<<"3"<<endl; } void fuu() { cout<<"4"<<endl; } int main() { A a; B b; A *p = &a; p->foo(); p->fuu(); p = &b; B *ptr = (B *)p; ptr->foo(); ptr->fuu(); return 0; } 1 2 4 3 不一定必须是积累指针访问派生类函数,反过来可以用派生类指针指向积累对象,通过强制类型转换,不过这种转换有危险

27 基类构造函数中对虚函数的调用不采用动态绑定。
2018年12月8日3时58分 Summary of the Allowed Assignments Between Base-Class and Derived-Class Objects and Pointers 特别说明: 基类构造函数中对虚函数的调用不采用动态绑定。 通过指针访问其他成员函数并调用虚函数时仍需动态绑定。 基类构造函数中对虚函数的调用不采用动态绑定。 如何理解? 总结来说:基类部分在派生类部分之前被构造,当基类构造函数执行时派生类中的数据成员还没被初始化。如果基类构造函数中的虚函数调用被解析成调用派生类的虚函数,而派生类的虚函数中又访问到未初始化的派生类数据,将导致程序出现一些未定义行为和bug。

28 虚函数动态绑定的各种情况 class A {public: A() { f(); } ~A(); virtual void f();
2018年12月8日3时58分 虚函数动态绑定的各种情况 class A {public: A() { f(); } ~A(); virtual void f(); void g(); void h() {f(); g(); } } ; class B: public A { public: B(); ~B(); void f(); }; A a; //调用A::A()和A::f a.f(); a.g(); a.h(); //调用A::h, A::f, A::g B b; //调用A::A(), A::f, B::B() b.f(); b.g(); b.h(); //调用A::h, B::f和A::g A *p; p=&a; p->f(); p->g(); p->h(); //调用A::h, A::f, A::g p=&b; p->f(); //调用B::f p->A::f(); //调用A::f p->g(); //调用A::g 静态绑定 p->h(); //调用A::h, B::f和A::g p=new B; //调用A::A(), A::f, B::B delete p; //调用A::~A() //调用B::~B, A::~A virtual ~A(); Virtual ~A()          直接的讲,C++中基类采用virtual虚析构函数是为了防止内存泄漏。具体地说,如果派生类中申请了内存空间,并在其析构函数中对这些内存空间进行释放。假设基类中采用的是非虚析构函数,当删除基类指针指向的派生类对象时就不会触发动态绑定,因而只会调用基类的析构函数,而不会调用派生类的析构函数。那么在这种情况下,派生类中申请的空间就得不到释放从而产生内存泄漏。所以,为了防止这种情况的发生,C++中基类的析构函数应采用virtual虚析构函数。

29 Topics 13.1 Introduction 13.2 Relationships Among Objects in an Inheritance Hierarchy 13.3 Type Fields and switch Statements 13.4 Abstract Classes and Pure virtual Functions 13.5 Case Study: Payroll System Using Polymorphism

30 13.3 Type Fields and switch Statements
shape类: enum ShapeType{ 长方形, 三角形, 椭圆形, 菱形}; ShapeType shapeType;// 数据成员 switch(pShape-> shapeType) case 长方形: downcast ptrShape to ptrRectangle; 画板

31 This is a square. This is a circle. This is a triangle. int main() {
enum ShapeType{ square,circle,triangle}; class Shape{ public: Shape(ShapeType x){shapeType=x;} void MoveTo(int new_pos_x,int new_pos_y){ position_x=new_pos_x; position_y=new_pos_y; } void draw(){cout<<"This is base class: Shape!"<<endl;};// 定义绘制方法 ShapeType shapeType; private: int position_x,position_y;//定义坐标 }; class Square:public Shape{ Square(ShapeType x):Shape(x){ }; void draw(){ cout<<"This is a squre."<<endl; } int length; }; class Circle:public Shape{ Circle(ShapeType x):Shape(x){ }; cout<<"This is a circle."<<endl; } int radius; }; class Triangle:public Shape{ Triangle(ShapeType x):Shape(x){ }; cout<<"This is a triangle."<<endl; int bottom; int highness; }; 2018年12月8日3时58分 int main() { Square sq(square); Circle ci(circle); Trigon tr(triangle); Shape *pShape[]={&sq,&ci,&tr}; for(int i=0;i<3;i++){ switch(pShape[i]->shapeType) { case square: { Square *ptrSquare = (Square*) pShape[i]; ptrSquare->draw();break; } case circle: { Circle *ptrCircle=(Circle*)pShape[i]; ptrCircle->draw();break;} case triangle: { Triangle *ptrTriangle=(Triangle*)pShape[i]; ptrTriangle->draw();break;} default: break; }} return 0; } int main() { Square sq(square); Circle ci(circle); Trigon tr(triangle); Shape *pShape[]={&sq,&ci,&tr}; for(int i=0;i<3;i++){ pShape[i]->draw(); } return 0; This is a square. This is a circle. This is a triangle.

32 Topics 13.1 Introduction 13.2 Relationships Among Objects in an Inheritance Hierarchy 13.3 Type Fields and switch Statements 13.4 Abstract Classes and Pure virtual Functions 13.5 Case Study: Payroll System Using Polymorphism

33 13.4 Abstract Classes and Pure virtual Functions --- 需求
class Shape{ public: virtual void draw() const; }; 一些成员函数对于基类来说是没有意义的,将其声明为纯虚成员函数的目的是要求派生类给出其实现。

34 13.4 Abstract Classes and Pure virtual Functions --- 纯虚函数
Pure Virtual Function(纯虚函数): A pure virtual function is specified by placing "= 0" in its declaration, as in virtual void draw() const = 0; 对于纯虚函数,不需要在类源码中给出其实现。

35 13.4 Abstract Classes and Pure virtual Functions --- 抽象类和具体类
Shape obj; // Error Rectangle objRectangle; Shape *ptr = &objRectangle; // OK Shape &ref = objRectangle; // OK 作用:为派生类提供一个基本框架或公共接口。 Concrete Class(具体类): 不包含纯虚函数,可以实例化。

36 13.4 Abstract Classes and Pure virtual Functions --- 小结
成员函数是否声明为虚函数,取决于是否需要多态性支持 虚函数是否声明为纯虚函数,取决于该函数对于当前类是否有意义,以及当前类是否需要实例化 基类 派生类 虚函数 has an implementation gives the derived class the option of overriding the function 纯虚函数 does not provide an implementation requires the derived class to override the function ( for that derived class to be concrete; otherwise the derived class remains abstract )

37 Topics 13.1 Introduction 13.2 Relationships Among Objects in an Inheritance Hierarchy 13.3 Type Fields and switch Statements 13.4 Abstract Classes and Pure virtual Functions 13.5 Case Study: Payroll System Using Polymorphism

38 13.5 Case Study: Payroll System Using Polymorphism
目的:输出各类员工的基本信息和薪金信息 Salaried employees( 定薪员工 ) Name, SSN, Weekly Salary Hourly employees( 计时工 ) Name, SSN, Wage per hour, Hours Commission employees( 佣金制员工 ) Name, SSN, Gross sales amount, Commission rate Base-salary-plus-commission employees( 带底薪的佣金制员工 ) Name, SSN, Gross sales amount, Commission rate, Base Salary

39 13.5 Case Study: Payroll System Using Polymorphism

40 13.5.1 Creating Abstract Base Class Employee
Employee Class Name, SSN: 各类员工的共有属性 print(): 输出Name, SSN等基本信息 — 虚函数 earnings(): 没有意义,要求派生类实现 — 纯虚函数 P 程序解读

41 13.5.1 Creating Abstract Base Class Employee

42 13.5.2 Creating Concrete Derived Class SalariedEmployee
继承Employee Class weeklySalary: 普通薪金制员工的独有属性 print(): Override基类函数, 输出基本信息和薪酬信息 earnings(): 必须Override基类的纯虚函数, 计算薪酬 P 程序解读

43 13.5.3 Creating Concrete Derived Class HourlyEmployee
继承Employee Class Wage, hours: 计时工的独有属性 print(): Override基类函数, 输出基本信息和薪酬信息 earnings(): 必须Override基类的纯虚函数, 计算薪酬 P 程序解读

44 13.5.4 Creating Concrete Derived Class CommissionEmployee
继承Employee Class grossSales, commisionRate: 佣金制员工的独有属性 print(): Override基类函数, 输出基本信息和薪酬信息 earnings(): 必须Override基类的纯虚函数, 计算薪酬 P 程序解读

45 13.5.5 Creating Indirect Concrete Derived Class BasePlusCommissionEmployee
继承CommissionEmployee Class, 间接继承Employee Class baseSalary: 带底薪的佣金制员工的独有属性 print(): Override基类(CommissionEmployee)函数, 输出基本信息和薪酬信息 earnings(): 选择Override基类的虚函数, 计算薪酬 P 程序解读

46 13.5.6 Demonstrating Polymorphic Processing
实例化四种类型员工,建立四个对象 通过对象名调用print和earnings函数(静态绑定) 通过基类指针调用print和earnings函数(动态绑定) 通过基类引用调用print和earnings函数(动态绑定) P 13.23 程序解读

47 Summary 多态在C++中的体现 纯虚函数 抽象类和具体类 静态多态性:通过函数重载和运算符重载实现,编译阶段
动态多态性:继承机制和虚函数,运行阶段 消息的多态;一个发送到基类对象的消息可以得到不同的解释。 纯虚函数 抽象类和具体类

48 Homework! 实验必选题目(交实验报告): 13.12, 13.16

49 13.5 Distinguish between inheriting interface and inheriting implementation. How do inheritance hierarchies designed for inheriting interface differ from those designed for inheriting implementation? ANS: When a class inherits implementation, it inherits previously defined functionality from another class. When a class inherits interface, it inherits the definition of what the interface to the new class type should be. The implementation is then provided by the programmer defining the new class type.

50 13.8 Distinguish between virtual functions and pure virtual functions.
ANS: A virtual function must have a definition in the class in which it is declared. A pure virtual function does not provide a definition. A pure virtual function is appropriate when it does not make sense to provide an implementation for a function in a base class (i.e., some additional derived-class-specific data is required to implement the function in a meaningful manner). Classes derived directly from the abstract class must provide definitions for the inherited pure virtual functions to become a concrete class; otherwise, the derived class becomes an abstract class as well.

51 13.10 How does polymorphism promote extensibility?
ANS: Polymorphism makes programs more extensible by making all function calls generic. When a new class type with the appropriate virtual functions is added to the hierarchy, no changes need to be made to the generic function calls. Only client code that instantiates new objects must be modified to accommodate new types.

52 Return class Base { public: Base() { Fuction(); }
2018年12月8日3时58分 class Base { public: Base() { Fuction(); } virtual void Fuction() { cout << "Base::Fuction" << endl; } }; class A : public Base A() { Fuction(); } { cout << "A::Fuction" << endl; } A a; Return

53 诊所管理系统


Download ppt "Object-Oriented Programming: Polymorphism"

Similar presentations


Ads by Google