220 likes | 362 Views
多态性. 多态性:用同样的接口访问功能不同的函数. class squared{ public: int squ(int) { … } double squ(double) { … } long squ(long) { … } }; void main() { squared value; cout<<value. squ( 3 ) ; cout<<value. squ( 3.5 ) ; cout<<value. squ( 6L ) ; }. 通过实参的特征区别. 函数联编. 静态联编:指系统在编译时就决定如何实现某个动作。 特点:
E N D
多态性 • 多态性:用同样的接口访问功能不同的函数 class squared{ public: int squ(int){… } double squ(double){…} long squ(long){…} }; void main() { squared value; cout<<value.squ(3); cout<<value.squ(3.5); cout<<value.squ(6L); } 通过实参的特征区别
函数联编 • 静态联编:指系统在编译时就决定如何实现某个动作。 • 特点: • 要求在程序编译时就知道调用函数的全部信息。 • 函数调用速度很快,效率高 • 通过函数重载而后运算符重载实现 • 动态联编:指系统在运行时动态实现某个动作 • 特点: • 要到程序运行时才能确定调用哪个函数 • 提供更好的灵活性、问题抽象性和程序易维护性 • 通过继承和虚函数实现
静态联编(通过对象的类型区别) • 重新在派生类中定义基类的函数 class point{… float area(){return 0.0;} }; class circle:public point{… float area( ){return 3.14159*r*r;} }; void main( ) { point p; p.area( ); 调用point类的函数 circle c; c.area( ); 调用circle类的函数 } 通过对象的类型区别
运行时的多态性 void main( ) { point *p; circle c; p=&c; p->area( ); 结果调用point类的area 指定关键字virtual,在运行时对函数动态联编,根据实际对象,调用该对象的成员函数 通过指针所引起的对普通成员函数的调用,仅仅由声明指针的类型决定,而与当时指针实际指向什么对象无关。 将关键字virtual放在类中area函数的函数说明之前: virtual float area( ){return 0.0;} 则结果调用circle类的area
虚函数 • 虚函数:为实现某种功能而假设的函数,虚函数只能是类中的一个成员函数,不能是静态成员 • 格式:virtual 数据类型 函数名(参数) • 说明: • 首先在基类中声明虚函数(需要具有动态多态性的基类) • 在派生类中某个成员函数的参数个数、相应类型和返回类型与基类同名的虚函数一样(即使没有virtual),则该成员函数为虚函数 • 不允许在派生类中定义与基类仅仅返回类型不同的函数 • 派生类中定义与基类虚函数同名但参数不同的函数,则该函数不为虚函数。 • 对虚函数保证在通过一个基类类型的指针调用一个虚函数时,系统对该调用进行动态联编。 • 对虚函数在通过一对象调用一个虚函数时,系统对该调用进行静态联编。 • 在调用中对虚函数使用成员名限定可强制对该函数的调用使用静态联编。
虚函数 void main( ) { circle c; circle &c1=c; c1.area( ); 结果调用circle类的area point &p=c; p.area(); 结果调用circle类的area p.point::area() 结果调用point类的area } • 使用普通对象调用虚函数,系统以静态联编方式完成对虚函数的调用。即用哪个类说明的对象,就调用在那个类中定义的虚函数。 • 为达到动态联编获得运行时多态性的效果,用指向第一次定义虚函数的基类对象的指针(引用)来调用虚函数
B b; D d; B *bp=&d; bp->vf1(); 调用D::vf1 bp->vf2(); 调用B::vf2 bp->vf2(5); 语法错 bp->f(); 调用B::f d.vf2(); 语法错 b=d; 赋值兼容 b.vf1(); 调用B::vf1 DD dd; D *dp=ⅆ dp->vf4(); 调用DD::vf4 dp->vf2(); 语法错 dp->vf3(); 调用DD::vf3 bp=ⅆ bp->vf2(); 调用DD::vf2 class B{ public: virtual void vf1(); virtual void vf2(); virtual void vf3(); void f(); }; class D:public B{ public: virtual void vf1(); 虚函数 void vf2(int); 普通重载 void f(); 普通重载 virtual void vf4(); 虚函数(基类相对) }; class DD:public D{ public: void vf4(); 虚函数 void vf2(); 虚函数 void vf3(); 虚函数
虚函数的访问权限 • 派生类中定义的虚函数的访问权限不影响对它进行动态联编。 • 一个类中的虚函数说明只对派生类中重定义的函数有影响,对它的基类中的函数无影响 class A{ public: void func( )… }; class B:public A{ public: virtual void func( )… }; class C:public B{ public : void func( )… }; C c c.func( ); c.B::func(); A &a=c; a.func(); B &b=c; b.func(); b.B::func();
在成员函数中调用虚函数 • 在一个基类或派生类的成员函数中可直接调用该类等级中的虚函数 class B:public A{ public: void fun3( ) {...fun4( );} void fun4( ) {…fun5( );} … 调用自身类中虚函数 调用自身类中函数 由于fun1是虚函数,所以thing可指向A类对象,也可指向B类对象 thing->fun1( ); 由于fun5不是虚函数,而是普通函数,总是先调用本类的成员函数(支配规则)。采用静态联编,调用A类的fun5(); thing->fun5( );
构造函数和析构函数调用虚函数 • 在构造函数和析构函数中调用虚函数时,采用静态联编,即调用的虚函数是自己的类或基类中定义的函数,不是任何在派生类中重定义的虚函数 • 执行C c时,先执行B的基类A的构造函数,再执行C的基类B的构造函数,最后调用C的构造函数。 • B的构造函数执行func(),由于A类和C类中均有,应执行哪个?应调用基类的虚函数 • 执行c.fun()时,调用func(),因为是C对象产生的,故调用C的函数。
空的虚函数 • 空虚函数为实现虚函数支配关系的不间断。 virtual void printOn(){}
纯虚函数与抽象类 • 基类中不能为虚函数给出一个有意义的定义,这时可将它说明为纯虚函数。 • 形式: class 类名{ virtual 类型 函数名(参数列表)=0; };
纯虚函数与抽象类 • 说明: • 抽象类:至少包含有一个纯虚函数的类,只能作为基类来派生新类,不能说明抽象类的对象。可以说明指向抽象类的指针(引用) • 从一个抽象类派生的类必须提供纯虚函数的实现代码,或在派生类中仍将它说明成纯虚函数。 • 在抽象类中也可定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但可通过派生类对象来调用不是纯虚函数的函数。 • 在成员函数内可以调用纯虚函数,但在构造函数和析构函数内不能调用
虚析构函数 • 只要基类的析构函数被说明为虚函数,则派生类的析构函数自动成为虚函数。而构造函数的调用意味着建立一个对象,这时必须确切的知道这个对象的类型,因此无意义 • 虚析构函数的说明: virtual ~类名(){…} 例:class A{ public: A( ){ } virtual ~A( ){cout<<“Destructor A”<<endl;} }; class B:public A{ public: B( ){ } ~B( ){cout<<“Destructor B”<<endl;} }; void main() { A *pa=new B; delete pa; }
class point{ float x,y; public: void setPoint(float i,float j){x=i;y=j;} virtual float area(){return 0.0;}}; class circle:public point{ float radius; float area(){return pi*radius*radius;} public: void setRadius(float r){radius=r;} }; void main() { circle c; c.setRadius(2.5); point &p=c; float a=p.area(); (float a=c.area();) cout<<"The area of the circle is "<<a<<endl;}
下列关于动态联编的描述中,错误的是( ) A)动态联编是以虚函数为基础的 B)动态联编是在运行时确定所调用的函数代码的 C)动态联编调用函数操作是指向对象的指针或对象引用 D)动态联编是在编译时确定操作函数的 • 关于虚函数的描述中,正确的是( ) A)虚函数是一个静态成员函数 B)虚函数是一个非成员函数 C)虚函数既可以在函数说明时定义,也可以在函数实现时定义 D)派生类的虚函数与基类中对应的虚函数具有相同的参数个数和类型
下面关于构造函数和析构函数的描述,错误的是( ) A)析构函数中调用虚函数采用静态联编 B)对虚析构函数的调用可以采用动态联编 C)当基类的析构函数是虚函数时,其派生类的析构函数也一定是虚函数 D)构造函数可以声明为虚函数 • 关于纯虚函数和抽象类的描述中,错误的是( ) A)纯虚函数是一种特殊的虚函数,它没有具体的实现 B)抽象类是指具有纯虚函数的类 C)一个基类中说明有纯虚函数,该基类的派生类一定不再是抽象类 D)抽象类只能作为基类来使用,其纯虚函数的实现由派生类给出
以下( )成员函数表示纯虚函数 A)virtual int vf(int); B)void vf(int)=0; C) virtual void vf()=0; D)virtual void vf(int){ } • 在下面程序中,A、B、C、D四句编译时出现错误的是 class A //A { public: //B A(){func();} //C virtual void func()=0; //D };
下列程序的运行结果如下: Bases cons. Deriveds cons. Deriveds des. Bases des. class Base{ public: Base(){cout<< “Bases cons.”<<endl;} {cout<< “Bases des.”<<endl;} }; class Derived:public Base{ public: Derived(){cout<< “Deriveds cons.”<<endl;} ~Derived(){cout<< “Deriveds des.”<<endl;} }; void main() { Base *ptr= delete ptr;}
#include <iostream.h> class Base{ public: virtual int func() {return 0;} }; class Derived:public Base{ public: int func() {return 100;} }; void main() { Derived d; Base &b=d; cout<<b.func()<<endl; cout<<b.Base::func()<<endl; }
class base1{ public: virtual int fun() {cout<< “base1”<<endl;}}; class base2{ public: void fun() {cout<< “base2”<<endl;}}; class derived:public base1,public base2{ public: void fun() {cout<< “derived”<<endl;}}; void main() { base1 *p1; base2 *p2; derived obj; p1=&obj; p1->fun(); p2=&obj; p2->fun();}
#include <iostream.h> class Base{ public: Base(){cout<< “Constructor of Base.”<<endl;fun();} virtual void fun(){cout<<“Base::fun() called.”<<endl;} }; class Derived:public Base{ public: Derived(){cout<<“Constructor of Derived.”<<endl;fun();} void fun(){cout<<“Derived::fun() called.”<<endl;} }; void main() { Derived obj; }