一、虚函数的定义和使用
可以在程序运行时通过调用相同的函数名而实现不同功能的函数称为虚函数。定义格式为:
virtual <type> FuncName(<ArgList>);
一旦把基类的成员函数定义为虚函数,由基类所派生出来的所有派生类中,该函数均保持虚函数的特性。 在派生类中重新定义基类中的虚函数时,可以不用关键字virtual来修饰这个成员函数 。虚函数是用关键字virtual修饰的某基类中的protected或public成员函数(当然private类的成员函数也可以是虚函数,他告诉用户你不能调用该函数,务必覆盖我它方能使用)。它可以在派生类中重新定义,以形成不同版本。只有在程序的执行过程中,依据指针具体指向哪个类对象,或依据引用哪个类对象,才能确定激活哪一个版本,实现动态聚束。
class A{ protected: int x; public: A(){x =1000;} virtual void print(){ cout <<“x=”<<x<<‘\t’; }//虚函数 }; class B:public A{ int y; public: B() { y=2000;} void print(){ cout <<“y=”<<y<<‘\t’; }//派生虚函数 }; class C:public A{ int z; public: C(){z=3000;} void print(){ cout <<“z=”<<z<<‘\n’; }//派生虚函数 }; void main(void ) { A a, *pa; B b; C c; a.print(); b.print(); c.print(); //静态调用 pa=&a; pa->print();//调用类A的虚函数 pa=&b; pa->print();//调用类B的虚函数 pa=&c; pa->print();}//调用类C的虚函数 }
二、关于虚函数,说明以下几点
1、当在基类中把成员函数定义为虚函数后,在其派生类中定义的虚函数必须与基类中的虚函数同名,参数的类型、顺序、参数的个数必须一一对应,函数的返回的类型也相同。若函数名相同,但参数的个数不同或者参数的类型不同时,则属于函数的重载,而不是虚函数。若函数名不同,显然这是不同的成员函数。
class Base { public : virtual int Set(int a, int b) { ..... } .... }; class Derive:public Base{ public : //两个Set()函数参数不一致,是重载,不是虚函数 int Set(int x, int y=0) { ..... } ..... };
2、实现这种动态的多态性时,必须使用基类类型的指针变量,并使该指针指向不同的派生类对象,并通过调用指针所指向的虚函数才能实现动态的多态性。
3、虚函数必须是类的一个成员函数,不能是友元函数,也不能是静态的成员函数。
4、在派生类中没有重新定义虚函数时,与一般的成员函数一样,当调用这种派生类对象的虚函数时,则调用其基类中的虚函数。
5、可把析构函数定义为虚函数,但是,不能将构造函数定义为虚函数。
6、虚函数与一般的成员函数相比较,调用时的执行速度要慢一些。为了实现多态性,在每一个派生类中均要保存相应虚函数的入口地址表,函数的调用机制也是间接实现的。因此,除了要编写一些通用的程序,并一定要使用虚函数才能完成其功能要求外,通常不必使用虚函数。
7、一个函数如果被定义成虚函数,则不管经历多少次派生,仍将保持其虚特性,以实现“一个接口,多个形态”。
三、虚函数的访问
1、用基指针访问虚函数时,指向其实际派生类对象重新定义的函数。实现动态聚束。
2、通过一个对象名访问时,只能静态聚束。即由编译器在编译的时候决定调用哪个函数。
class Point{ float x,y; public: Point(){} Point(float i,float j){ x=i; y=j; } virtual float area(void) { return 0.0; }//声明为虚函数 }; const float Pi=3.14159; class Circle:public Point{ //类Point的派生类 float radius; public: Circle(float r){ radius=r; } float area(void) { return Pi*radius*radius;}//虚函数再定义 }; void main(void) { Point *pp; //基类指针,可以将派生类对象的地址赋给基类指针 Circle c(5.4321); cout<<c.area()<<endl; pp = &c; cout << pp->area() << endl; cout << c.Point::area() << endl; cout << c.Circle::area () << endl; } 运行结果: 92.7011 92.7011 0 92.7011
可见,利用对象名进行调用与一般非虚函数没有区别。
除非注明,文章均为CppLive 编程在线原创,转载请注明出处,谢谢。