第八章 多态性和虚函数

  • 格式:doc
  • 大小:51.50 KB
  • 文档页数:7

第八章多态性和虚函数一、静态联编和动态联编1. 联编是指一个计算机程序自身彼此关联的过程.按照联编所进行阶段的不同,可分为静态联编和动态联编。

2. 静态联编是指工作在编译连接阶段,这种联编又称为早期联编,因为这种联编过程在程序开始前完成.在编译时所进行的这种联编也称静态约束,它在编译时就解决了程序中的操作调用与执行该操作代码间的关系。

3. 静态联编的程序在编译阶段并不知道将要调用的函数,只有在程序执行时才能确定要调用的函数。

例1: class Point{ int x,y; public: Point(int x1,int y1){ x=x1;y=y1; }int area() const{ return 0; } };class Rect:public Point{ int l,w; public:Rect(int x1,int y1,int l1,int w1):Point(x1,y1){ l=l1;w=w1;}int area() const{ return l*w; } };void fun(Point &p){ cout<<p.area()<<endl; }void main(){ Rect rec(2,4,10,6); fun(rec); }执行结果:0在fun()函数中,p所引用的对象执行的area()操作被关联到Point::area()的定义代码上,即始终调用Point类的area()成员函数,这是静态联编的结果。

我们期望的是p所引用的对象执行的area()操作束定到Rect类的area()函数上,这是静态联编办不到的。

4. 为了确切知道将要调用的函数,要求联编工作要在程序运行时进行.在程序运行时进行的联编工作叫动态联编、或晚期联编,实际是进行动态标识。

5. 静态联编和动态联编属于多态性,它们是在不同阶段对不同实现进行的不同选择。

上例实际是对fun()函数参数的多态性的选择,该函数的参数是一个类的对象的引用。

静态联编和动态联编的选择实际上是在选择它的静态类型和动态类型,联编是对这个引用的多态性的选择。

二、虚函数1. 虚函数是动态联编的基础,是非static的成员函数。

说明虚函数的方法:virtual 类型说明符函数名(参数表)2. 如果某个类中的一个成员函数被说明为虚函数,则该成员函数在派生类中可能有不同定义。

当使用这个成员函数操作指针或引用所标识的对象时,将对该成员函数调用采取动态联编方式,即在运行时进行关联或束定。

3. 动态联编只能通过指针或引用标识对象来操作虚函数,如果采用一般类型的标识对象来操作虚函数,将采用静态联编方式调用虚函数。

例2: class Point{ int x,y; public: Point(int x1,int y1){ x=x1;y=y1; }virtual int area() const{ return 0; } };class Rect:public Point{ int l,w; public:Rect(int x1,int y1,int l1,int w1):Point(x1,y1){ l=l1;w=w1;}virtual int area() const{ return l*w; } };void fun(Point &p){ cout<<p.area()<<endl; }void main(){ Rect rec(2,4,10,6); fun(rec); }执行结果:60实际上,只要说明类Point的area()函数为虚函数就够了,因为基类中的虚函数在派生类中自然是虚函数。

此时,在fun()函数中的引用对象参数p被动态联编,该函数体内调用的area()函数是在运行中束定的,它被确定为Rect类中的area()函数。

4. 动态联编的实现需要3个条件:(1)类之间为基类和派生类的关系。

(2)要有虚函数。

(3)调用虚函数操作的是指向对象的指针或对象引用,或者由成员函数调用虚函数。

5. 当派生类的虚函数和基类中对应的虚函数的参数不同,派生类的虚函数将丢失虚特性,变成重载函数。

例3: class A{ public: virtual void fun(int i){ cout<<”class base:”<<i<<endl; } };class B:public A{ public: void fun(double d){cout<<”class base:”<<d<<endl; } };void main(){ A a,*p=&a; B b; (*p).fun(1); p=&b; (*p).fun(2.5); }执行结果:class base:1 class base:2三、纯虚函数和抽象类1. 纯虚函数是一种特殊的虚函数,它的定义留给该基类的派生类去做。

一般格式为:class 类名{ virtual 类型函数名(参数表)=0; …};例4: class base{ protected: int x,y; public:virtual void setx(int i,int j=0){ x=i; y=j; }virtual void disp()=0; };class Square:public base{ public: void disp(){ cout<<”x=”<<x<<”:”;cout<<”x square=”<<x*x<<endl; } };class Cube:public base{ public: void disp(){ cout<<”x=”<<x<<”:”;cout<<”x cube=”<<x*x*x<<endl; } };class Chpow:public base{ public: void disp(){cout<<”x=”<<x<<”y=”<<y<<”:”;cout<<”pow(x,y)=”<<pow(double(x),double(y))<<endl;} };void main(){ base *ptr; Square B; Cube C; Chpow D;ptr=&B; ptr->setx(5); ptr->disp();ptr=&C; ptr->setx(6); ptr->disp();ptr=&D; ptr->setx(3,4); ptr->disp(); }执行结果:x=5:x square=25 x=6:x cube=216 x=3 y=4:pow(x,y)=812. 带有纯虚函数的类称为抽象类,是为了抽象和设计的目的而建立的,处于继承层次结构的较上层。

主要作用是将有关子类组织在一个继承层次结构中,由它来为它们提供一个公有的根,相关的子类是从这个根派生出来的。

3. 抽象类只能用作其他类的基类,不能建立抽象类对象。

抽象类不能用作参数类型、函数返回类型或显示转换的类型。

可以说明指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性。

例5: const double PI=3.14159;class shapes{ protected: int x,y; public:void setvalue(int d,int w=0){ x=d; y=w; }virtual void disp()=0; };class square:public shapes{ public: void disp(){cout<<”矩形面积:”<<x*y<<endl; } };class circle:public shapes{ public: void disp(){cout<<”圆形面积:”<<PI*x*x<<endl; } };void main(){ shapes *ptr[2]; square s1; circle c1;ptr[0]=&s1; ptr[0]->setvalue(10,5); ptr[0]->disp();ptr[1]=&c1; ptr[1]->setvalue(10); ptr[1]->disp(); }执行结果:矩形面积:50 圆面积:314.159四、虚析构函数1. 在析构函数前加上virtual关键字说明,则为虚析构函数。

如果一个基类的析构函数被说明为虚析构函数,则派生类中的析构函数也是虚析构函数,不管它是否使用了virtual进行说明。

2. 说明虚析构函数的目的是在使用delete运算符删除一个对象时,能保证析构函数被正确执行。

因为设置虚析构函数后,可以使用动态联编方式选择析构函数。

例6: class A{ public: virtual ~A(){ cout<<”调用A::~A()”<<endl; } };class B:public A{ char *buf; public: B(int i){ buf=new char[i]; }virtual ~B{ delete [] buf; cout<<”调用B::~B() ”<<endl; } };void fun(A *a){ delete a; }void main(){ A *a=new B(10); fun(a); }执行结果:调用B::~B() 调用A::~A()如果类A中的析构函数不用虚函数,则输出结果为:调用A::~A()因为基类的析构函数说明为虚函数时,调用fun(a)函数,执行delette a; 语句时采用动态联编,调用它的基类的析构函数。

当不说明基类的析构函数为虚函数时,delete隐含对析构函数的调用,也就是不调用它的基类的析构函数。

习题 1. 实现运行时的多态性要使用( )。

2. 如果一个类至少有一个纯虚函数,则称该类为()。

3. ()是一个在基类中说明的虚函数,它在该基类中没有定义,但要求任何派生类都必须定义自己的版本。

4. 关于动态联编的说法错误的是()。

A. 是以虚函数为基础的B. 是在运行时确定所调用的函数代码的C. 调用函数操作是指向对象的指针或对象引用D. 是在编译时确定执行函数的5. 关于虚函数的说法正确的是()。