1、面向对象程序设计讲义,版权所有, 1997 (c) Dale Carnegie & Associates, Inc.,第 12 章,12.1 多态性在C+中的体现所谓多态性就是当不同对象收到相同的消息时产生不同 的动作。12.1.1 编译时的多态性 1. 在一个类中说明的重载 2. 基类成员函数在派生类中重载 12.1.2 运行时的多态性 先看下面的例子:,第 12 章 多态性与虚函数,#include #include class pointprotected:int x,y;public:point(int x,int y) point:x=x; point:y=y; virtual vo
2、id show() / 定义虚函数 putpixel(x,y,getcolor(); ;,class circles:public point int radius;public:circles(int x,int y,int radius):point(x,y)circles:radius=radius;void show()circles(x,y,radius); ;,main() point p1(10,10);circles c1(100,100,50);setcolor(14);point *ptr; /定义指向基类的指针ptr= /调用c1对象的show() ,12.2 虚 函 数
3、 12.2.1 对象指针 1. 一般对象的指针语法与指向一般变量的指针相同。 2. 引入派生类后的对象指针任何被说明为指向基类的指针都可以指向它的公有派生类。 使用派生类对象指针时应注意的问题: (1)可以用一个声明让指向基类对象的指针指向它的公有派生 的对象。禁止指向私有派生的对象。 (2)不能将一个声明为指向派生类对象的指针指向其基类的一个对象。,(3)声明为指向基类对象的指针,当其指向派生类对象时,只能利用它来直接访问派生类中从基类继承来的成员,不能访问公有派生类中特定的成员。 12.2.2 为什么要引入虚函数 #include class base public:void who()
4、cout“this is the class of base !n”; ,class derive1:public base public:void who() cout“this is the class of derive1 !n”; ;class derive2:public base public:void who() cout“this is the class of derive2 !n”; ;,main() base obj1,*p;derive1 obj2;derive2 obj3;p= ,运行结果: this is the class of base! this is the
5、 class of base! this is the class of base! this is the class of derive1 ! this is the class of derive2 !从结果可以看出,通过指针引起的普通成员函数调用,仅仅与指针的类型有关,而与此刻正指向什么对象无关。,12.2.3 虚函数的定义及使用1. 虚函数的定义 #include class base /.public:virtual void who() /定义虚函数 cout“ base!n”; ; class frist:public base /.public:void who() /重新定
6、义虚函数 cout“ the first derivationn”; ;,class second:public base /.public:void who() /重新定义虚函数 coutwho(); /调用base类的who( )版本,ptr= /调用second类的who()版本 运行结果:base the first derivationthe second derivation,2. 虚函数与重载函数的关系 3. 多继承中的虚函数 #include class a public:virtual void f() /定义f()为虚函数 cout“class an”; ; class b
7、 public:void f() /定义f()为一般函数 cout“class bn”; ;,class aa: public a, public b public:void f() coutf(); /调用a类的f()ptr2= /调用b类的f(),ptr1= /调用b类的f(),此处f()为非虚函数,而ptr2/又为b的指针 运行结果: class a class b class aa class b,若一个派生类,它的多个基类中有公共的基类,在公共基类 中定义一个虚函数,则多级派生以后仍可以重新定义虚函数。 使用级联式派生时要注意,指向派生类的指针不能继承。也 就是说,基类的指针可以指向
8、它的派生类,但不能再指向它 的派生类的派生类。4. 基类构造函数调用虚函数自学,12.2.4 虚函数举例 例 3 /- #include #pragma hdrstop #include “U12_2_4_3.h“ /- #pragma package(smart_init) #pragma resource “*.dfm“ Tf12_2_4_3 *f12_2_4_3; /*- /enum bool false,true;,struct element /定义链表中的结点结构int val;element *next; ; class list /定义链表类element *elems;pub
9、lic:list()elems=0;list();virtual bool insert(int); /定义虚函数virtual bool deletes(int); /定义虚函数bool contain(int);void print();,class set:public list int card;public:set()card=0;bool insert(int); /重定义虚函数bool deletes(int); /重定义虚函数 ; list:list() element *tmp=elems;for(element *elem=elems;elem!=0;)tmp=elem;e
10、lem=elem-next;delete tmp; ,bool list:insert(int val) /定义list类中插入元素的成员函数 element *elem=new element; /为新元素分配内存if(elem!=0)elem-val=val; /将元素插入链表头elem-next=elems;elems=elem;return true;else return false; ,bool list:deletes(int val) /定义list类中删除元素的成员函数 if(elems=0)return false; /若表为空,返回falseelement *tmp=el
11、ems;if(elems-val=val) /若待删除的元素为链表头元素elems=elems-next;delete tmp;return true;else,for(element *elem=elems;elem-next!=0;elem-next)if(elem-next-val=val) /循环查找待删除元素tmp=elem-next;elem-next=tmp-next;delete tmp;return true;return false; ,bool list:contain(int val) /判元素val在链表中是否存在if(elems=0)return false;if(
12、elems-val=val)return true;elsefor(element *elem=elems;elem-next!=0;elem=elem-next)if(elem-next-val=val)return true;return false;,void list:print() if(elems=0)return;int i=1;static int j=0;j+;for(element *elem=elems;elem!=0;elem=elem-next)f12_2_4_3-Canvas-TextOut(30*i+,20*j,IntToStr(elem-val);,bool s
13、et:insert(int val) /在set类中的insert的重定义版本if(!contain(val) ,bool set:deletes(int val) /在set类中的deletes重定义版本if(list:deletes(val) /调用基类中的此函数版本-card;return true;return false; /*- _fastcall Tf12_2_4_3:Tf12_2_4_3(TComponent* Owner): TForm(Owner) ,void _fastcall Tf12_2_4_3:btnRunClick(TObject *Sender) list *p
14、tr,list1; /定义基类对象list1和基类指针ptrset set1; /定义set类对象ptr=,ptr-insert(456);ptr-insert(23);ptr-print(); /调用list类中的成员函数 /- void _fastcall Tf12_2_4_3:btnEndClick(TObject *Sender) Close(); /-,12.3 抽 象 类12.3.1 纯虚函数与抽象类 1. 纯虚函数的概念 定义形式: virtual type func_name(参数表)=0; 注意事项:在基类中定义为纯虚函数的函数,在任何派 生类中都必须定义自己的版本,否则将引
15、起语法错误。,2. 抽象类的概念 如果类中至少有一个纯虚函数,那么就称该类为抽象类。 使用抽象类的几点规定: (1)抽象类只能用作其它类的基类,不能建立抽象类对象。 (2)抽象类不能用作参数类型、函数返回类型或显式转换的类型。 (3)可以声明指向抽象类的指针和引用,此指针可以指 向它的派生类,进而实现多态性。,举例说明如下: class figure /public:virtual draw()=0;/ ; figure a; /错误,不能建立抽象类对象 figure *ptr; /正确,可以声明指向抽象类的指针 figure f(); /错误,抽象类不能作为函数的返回类型 void g(fi
16、gure); /错误,抽象类不能作为函数的参数类型 figure /正确,可以声明抽象类的引用,12.3.2 纯虚函数多态性的体现 例 2 #include class container /定义一个抽象类protected:double radius;public:container(double radius) container:radius= radius; virtual double surface_area()=0; /纯虚函数virtual double volume()=0; /纯虚函数 ;,class cube:public container /定义正方体类public:
17、cube(double radius):container(radius) ;double surface_area(); /虚函数在本类中的定义版本double volume(); ; class sphere:public container /定义球体类public:sphere(double radius):container(radius) ;double surface_area(); /虚函数在本类中的定义版本double volume(); ;,class cylinder:public container /定义圆柱体类double height;public:cylinde
18、r(double radius,double height):container(radius) cylinder:height=height; double surface_area(); /虚函数在本类中的定义版本double volume(); ; double cube:double surface_area() return(radius*radius*6); ,double cube:volume() return(radius*radius*radius); double sphere:double surface_area() return 4*3.1416*radius*radius; double sphere:volume() return 3.1416*radius*radius*radius*4/3; ,double cylinder :double surface_area() return 3.1416*2*radius*(height+radius); double cylinder:volume() return 3.1416*radius*radius*height; ,main() container *ptr; /cube obj1(5);sphere obj2(5);cylinder obj3(5,5);ptr= ,