7.1 多态性概述7.2 虚函数
7.3 抽象类7.4 综合实例
第
7章
多态与虚函数
一种语言若不支持多态,则不能称之为面向对象的程序设计语言。本章要讨论的多态性与前面我们介绍的继承、封装等一样,都是面向对象程序设计语言中的重要特征。多态性与继承是密不可分的,本章所讲的多态性指在继承类中与基类同名、同参数、同类型函数的不同行为。
一般来说,C++语言支持两种不同类型的多态:编译时多态和运行时多态。本章讨论的内容主要集中在运行时多态及其实现。
【7.1 多态性概述】
多态就是指不同的对象接受到相同的消息时产生不同的响应动作,即对应相同的函数名,却执行了不同的函数体(当然,这些函数体还是要事先定义好,以便调用)。
种把程序标示符与和一个存储地址相联系的过程,称为联编(binding,又译为绑定)。
静态联编:指这种联编在编译阶段完成的,由于联编过
程是在程序运行前完成的,所以又称为早期联编。静态联编能够实现编译时多态。动态联编:指这种联编要在程序运行时动态进行,所以
又称为晚期联编。动态联编可以实现运行时多态。
要实现静态联编,在编译阶段就必须确定标示符(如函数名)和代码之间的对应关系。
重载多态:是函数重载。
强制转换多态:不同类型的数据类型进行
混合运算时要进行的强制类型转换。包含多态:指在基类及其派生类族中同名
函数的不同函数实现,及其在运行时的不同响应。编译时多态和运行时多态的严格划分
都是通用多态
都是专用多态
【7.1 多态性概述】
支持两种编译方式
【7.2 虚函数】
例7-1通过基类指针访问派生类对象
EXAMPLE7_01.H
#ifndef EXAMPLE7_01_H
#define EXAMPLE7_01_H
#include
class Base //声明基类Base
{
private:
double dblBaseTest;
public:
Base(double dblInitial=0.0){dblBaseTest=dblInitial;}
~Base(){}
void setBase(double dblSet){dblBaseTest=dblSet;}
double getBase(){return dblBaseTest;}
void display(); //用于显示基类中的私有数据成员};
class FromBase:public Base //从基类Base 以公有派生类FromBase {
private:
double dblFromTest;public:
FromBase(){}
FromBase(double dblInitBase,double dblInitFrom):Base(dblInitBase){dblFromTest=dblInitFrom;}~FromBase(){}
void setFrom(double setFrom){dblFromTest=setFrom;}double getFrom(){return dblFromTest;}
void display(); //显示派生类的私有数据成员};#endif
//类的声明结束
EXAMPLE7_01.CPP //类的实现开始
#include
#include
cout<<″Now in Base class, we display its private number:″< cout<<″dblBaseTest :″< } void FromBase::display() { cout<<″Now in FromBase class, we display its private number:″< cout<<″dblFromTest :″< } // 类的实现结束 EXAMPLE7_1.CPP //主程序开始 #include #include void main() { Base myBase(1.0); FromBase myFrom(2.0,3.0); Base *ptrBase=&myBase; //声明指向基类对象的指针 myBase.display(); ptrBase->display(); myFrom.display(); ptrFrom->display(); ptrBase=&myFrom; //将基类指针指向派生类对象 ptrBase->display(); } //程序结束 Now in Base class, we display its private number: dblBaseTest :1.000000 Now in Base class, we display its private number: dblBaseTest :1.000000 Now in FromBase class, we display its private number: dblFromTest :3.000000 Now in FromBase class, we display its private number: dblFromTest :3.000000 Now in Base class, we display its private number: dblBaseTest :2.000000 //EXAMPLE7_02.H //类的声明及实现开始#ifndef EXAMPLE7_02_H #define EXAMPLE7_02_H #include class Fruit //水果类的声明及其实现{ public: Fruit(){}~Fruit(){} virtual void dispFruitName() //用virtual 声明虚函数dispFruitName() {cout<<″It′s fruit class!″< //的实现 virtual void eatFruit() //用virtual 声明虚函数eatFruit() {cout<<″Can not eat an abstract fruit!″< //现。 }; 水果类 例7-2 class Apple:public Fruit //公有派生类Apple { private: char cAppleName[20]; public: Apple(){} ~Apple(){} void getAppleName(char applename[20]){strcpy(cAppleName,applename);} void dispAppleName(){cout<<″The apple name is:″< //虚函数dispFruitName()的实现void eatFruit(){cout<<″Oh,it′s some acid!″< //eatFruit()的实现 }; class Orange:public Fruit //公有派生类Orange { private: char cOrangeName[20]; public: Orange(){} ~Orange(){} void getOrangeName(char orangename [20]){strcpy(cOrangeName,orangename);} void dispOrangeName(){cout<<″The orange name is:″< void dispFruitName(){cout<<″The fruit name is orange.″< void eatFruit(){cout<<″Haha,it′s sweet!″< // eatFruit()的实现}; #endif //类的声明及实现结束 //EXAMPLE7_2.CPP //主程序开始 #include #include ″EXAMPLE7_02.H″ void main() { Apple myapple,*ptrapple; //声明苹果类的对象及指针Orange myorange,*ptrorange; //声明橘子类的对象及指针myapple.getAppleName(″Guoguang″); myorange.getOrangeName(″Huangyan″); ptrfruit=&myfruit; ptrapple=&myapple; ptrorange=&myorange; ptrfruit->dispFruitName(); ptrfruit->eatFruit(); ptrapple->dispFruitName(); ptrapple->dispAppleName(); ptrapple->eatFruit(); ptrorange->dispFruitName(); ptrorange->dispOrangeName(); ptrorange->eatFruit(); ptrfruit=ptrapple; //将基类指针指向派生类对象ptrfruit->dispFruitName(); //执行派生类的成员函数 ptrfruit->eatFruit(); ptrfruit=ptrorange; //将基类指针指向派生类对象 ptrfruit->dispFruitName(); //执行派生类的成员函数 ptrfruit->eatFruit(); } //主程序结束 说明:声明了基类指针,就可以使不同的派生类对象产生不同 的函数调用,实现了程序的运行时多态。运行时多态应该使用 虚函数,并通过指针、引用或者成员函数调用虚函数。 It′s fruit class! Can not eat an abstract fruit!The fruit name is apple. The apple name is:Guoguang Oh,it′s some acid! The fruit name is orange. The orange name is:Huangyan Haha,it′s sweet! The fruit name is apple. The apple name is:Guoguang Oh,it′s some acid! The fruit name is orange. The orange name is:Huangyan Haha,it′s sweet! 声明虚成员函数的语法是正如上例中我们所看到的: virtual 函数类型函数名称(形式参数表); 虚函数的声明只能出现在类声明时的函数原型声明中。在派生类中可注 (续) 【7.3 抽象类】 函数无法具体实现(或不必实现)。这样的函数可以声明为纯虚函数。对于纯虚函数,可以不必定义它的函数体。含有纯虚函数的类被称为抽象类。 设计抽象类的目的就是为了多态地使用它的成员函数,由此为整个类族规定统一的接口形式。 纯虚函数的语法形式是: virtual 函数类型函数名(参数表)=0; 注 特点是在函数名及参数表后面多了一个“=0”。 一旦在基类中声明了纯虚函数,该基类就成为抽象类,若该类的某个派生类没有给出基类中的纯虚函数的全部实现,则该派生类依然是抽象类。 可以声明指向抽象类的指针,虽然该指针不能指向任何抽象类的对象(因为不存在),但可以通过该指针获得对派生类成员函数的调用。#ifndef EXAMPLE7_03_H #define EXAMPLE7_03_H #include Fruit(){}~Fruit(){} virtual void dispFruitName()=0; //声明为纯虚函数virtual void eatFruit()=0; //声明为纯虚函数}; class Apple:public Fruit //派生类为非抽象类改进后的水果类——抽象类 说明:水果本身是一个抽象的概念。所以可以考虑用抽象类来实现水果类。例7-3 private: char cAppleName[20]; public: Apple(){} ~Apple(){} void getAppleName(char applename[20]){strcpy(cAppleName,applename);} void dispAppleName(){cout<<″The apple name is:″< //中的纯虚函数void eatFruit(){cout<<″Oh,it′s some acid!″< //函数 }; class Orange:public Fruit //派生类为非抽象类 { private: char cOrangeName[20]; public: Orange(){} ~Orange(){} void getOrangeName(char orangename [20]){strcpy(cOrangeName,orangename);} void dispOrangeName(){cout<<″The orange name is:″< void dispFruitName(){cout<<″The fruit name is orange.″< //类中的纯虚函数void eatFruit(){cout<<″Haha,it′s sweet!″< //数}; #endif //主程序 #include #include ″EXAMPLE7_03.H″ void main() { Fruit *ptrfruit; //声明指向虚基类的指针 Apple myapple; Orange myorange; myapple.getAppleName(″Guoguang″); myorange.getOrangeName(″Huangyan″); ptrfruit=&myapple; //将虚基类指针指向派生类的对象ptrfruit->dispFruitName(); ptrfruit->eatFruit(); ptrfruit=&myorange; //将虚基类指针指向派生类对象ptrfruit->dispFruitName(); ptrfruit->eatFruit(); } The fruit name is apple. The apple name is:Guoguang Oh,it′s some acid! The fruit name is orange. The orange name is:Huangyan Haha,it′s sweet! 求不同类型图形的位置和面积 //EXAMPLE7_04.H //类声明 #ifndef EXAMPLE7_04_H #define EXAMPLE7_04_H #include class Shape //声明抽象类作为基类 { protected: double x; double y; public: Shape(){} Shape(double xx,double yy);~Shape(){} virtual void dispName()=0; //声明纯虚函数virtual void dispArea()=0; //声明纯虚函数virtual void dispPos()=0; //声明纯虚函数}; 说明:在基类中声明3个虚函数,dispName(),dispArea()和dispPos()。这些虚函数在其派生类Circle 和Rectangle 中获得不同的实现。例7-4 class Circle:public Shape //声明派生类Circle (续){ private: double radius; double area; public: Circle(double cx=1,double cy=1,double rad=1); ~Circle(); void dispName(); void dispArea() void dispPos(); }; class Rectangle:public Shape //声明派生类Rectangle { private: double length; double width; double area; public: