一、多态性
派生类对象可以替代基类对象为基类的引用初始化或赋值。
函数的多态性其实就是对函数不同形式的声明的一种灵活应用。比如说,我们同名不同参数的函数就是对函数的一种多态性表现;同名同参就是函数的覆盖;如果我们用不同类型的参数和个数来声明不同或相同的函数,那么程序会根据我们调用实参的个数和类型进行匹配调用之前声明的函数模型,进行运算求值。
二、虚函数
在类的继承层次结构中,在不同的层次中可以出现同名同参(类型、个数)都相同的函数。在子类中调用父类的成员方法,可以使用子类对象调用时使用父类的作用域实现。
虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
举一个实例来说明使用虚函数与不使用虚函数的区别,基类和派生类中都有同名函数。
不使用虚函数:
- //
- // Student.h
- // Programs
- //
- // Created by bo yang on 4/17/12.
- // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
- //
- #ifndef Programs_Student_h
- #define Programs_Student_h
- using namespace std;
- class Student
- {
- public:
- Student(int ,string,float);
- void display();
- protected:
- int num;
- string name;
- float score;
- };
- #endif
- //
- // Student.cpp
- // Programs
- //
- // Created by bo yang on 4/17/12.
- // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
- //
- #include <iostream>
- #include <string>
- #include "Student.h"
- using namespace std;
- //定义构造函数
- Student::Student(int n,string nam,float s)
- {
- num=n;
- name=nam;
- score=s;
- }
- void Student::display()
- {
- cout<<"num:"<<num<<"\n name:"<<name<<"\n score:"<<score<<"\n"<<endl;
- }
- //
- // Graduate.h
- // Programs
- //
- // Created by bo yang on 4/17/12.
- // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
- //
- #ifndef Programs_Graduate_h
- #define Programs_Graduate_h
- #include "Student.h"
- #include <string.h>
- using namespace std;
- class Graduate:public Student
- {
- public:
- Graduate(int ,string ,float,float);
- void display();
- private:
- float pay;
- };
- #endif
- //
- // Graduate.cpp
- // Programs
- //
- // Created by bo yang on 4/17/12.
- // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
- //
- #include <iostream>
- #include "Graduate.h"
- #include "Student.h"
- using namespace std;
- void Graduate::display()
- {
- cout<<"num:"<<num<<"\nname:"<<name<<"\nscore:"<<score<<"\npay="<<pay<<endl;
- }
- Graduate::Graduate(int n,string nam,float s,float p):Student(n,nam,s),pay(p)
- {
- }
- //
- // main.cpp
- // Student&Graduate
- //
- // Created by bo yang on 4/17/12.
- // Copyright (c) 2012 __MyCompanyName__. All rights reserved.
- //
- #include <iostream>
- #include "Student.h"
- #include "Graduate.h"
- using namespace std;
- int main(int argc, const char * argv[])
- {
- Student s1(1000,"David",100);
- Graduate g1(2000,"Jarry",50,20);
- Student *p=&s1;
- p->display();
- p=&g1;
- p->display();
- return 0;
- }
说明:定义一个Student类,让Graduate类继承Student类后,两个类中共同定义了一个display()方法,因为是在基类和派生类中分别定义的同名同参类,所以没有冲突,但是在调用的时候就出现了问题。我们在主函数中定义一个指针p指向父类对象的首地址,然后用指针p调用display()函数,没问题,我们可以打印出我们想要的数据。然后用指针p指向派生类Graduate的对象首地址,再次调用display()方法,你会发现Graduate中继承父类的成员对象没有被重写,还是父类中成员值,Graduate自己的成员打印了出来是没有问题的,在此说明通过一个指针指向不同的类分别调用不同层次中的同名同参类是不行的。这就需要用到虚函数来解决此问题了。
我们在上述程序中作一点修改:在Student类中声明display()时,加一个关键字virtual即
virtual void display();
这样就把Student类display()声明成一个虚函数。这样就可以实现刚才想要的效果了。
说明:基类指针本来是用来指向基类对象的,如果用它指向派生类对象,则进行指针类型转换,将派生类对象的指针先转换为基类的指针,所以基类的指针指向的是派生类对象中的基类部分。
程序修改前,是无法通过基类指针去调用派生类对象中的成员函数的。虚函数突破这一限制,在派生类的基类部分中,派生类的虚函数取代了基类原来的虚函数,因此在使基类指针指向派生类对象后,调用虚函数时就调用了派生类的虚函数。只有用virtual声明了虚函数后才具有以上作用。如果不声明为虚函数,企图通过基类指针调用派生类的非虚函数是不行的。
把基类的某个成员函数声明为虚函数后,允许在其派生类中对该函数重新定义,赋予它新的功能,并且可以通过指向基类的指针指向同一类族中不同类的对象,从而调用其中的同名函数。由虚函数实现的动态多态性就是:同一类族中不同类的对象,对同一函数调用作出不同的响应。
虚函数的使用方法:
1、 在基类用virtual声明成员函数为虚函数。这样就可以在派生类中重新定义此函数,为它赋予新的功能,方便使用。
2、 在派生类中重新定义此函数,要求函数名、函数类型、函数参数个数和类型全部与基类的虚函数相同,并根据派生类的需要重新定义函数体。
3、 在主函数中定义一个基类指针指向基类对象,引用基类函数;指向派生类对象,引用派生类函数。
C++规定:一个成员函数被声明为虚函数时,派生类中的同名函数都自动成为虚函数。所以派生类中同名函数加不加virtual都是可以的,习惯上添加,程序易懂。
注意:如果在派生类中没有对基类的虚函数重新定义,则派生类简单地继承其直接基类的虚函数。
通过虚函数与指向基类对象的指针变量的配合使用,就能方便地调用同一类族中不同类的同名函数,只要先用基类指针指向即可。如果指针不断地指向同一类族中的不同类的对象,就能不断调用不同类的对象中的同名函数。
如果在基类中定义的非虚函数在派生类中被重新定义,如果用基类指针调用该成员函数,系统会调用对象中基类部分的成员函数;如果用派生类的指针调用该成员函数,则系统对调用派生类对象中的成员函数,这不是多态,是不同类型的指针,没有用到虚函数