之前被某公司面试官问过一次虚函数是什么。至今印象深刻,因为当时并没有回答上来。于是趁今天有空,把虚函数翻了翻,发个blog总结一下。
虚函数的作用
虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
——引自经典教材谭老师的红皮书
那么,虚函数就是解决类的继承过程中同名函数的处理问题,详细点说就是处理不同派生层次上的同名函数问题。举栗子来说,对于一个基类Student和其子类Graduate,都有一个同名函数display() (陈列出实例的详细信息)。对于学生这个类而言,属性有(学号,姓名,分数)这3个,对于毕业生而言,属性除这3个以外还有一个工资。所以,这两个类的同名函数display()具有不一样的功能,前者访问3个属性并打印出来,后者访问4个属性并打印出来。那么怎么使这两个同名函数不冲突呢?其实也好办,不使用虚函数也好办,我们只需定义不同的实例名然后通
过实例名调用函数就可以了。打比方,
Student s1(x,x,x);
Graduate g1(x,x,x,x);
s1.display();
g1.display();
这样就可以使两个同名函数不冲突,这就有点像JAVA编程风格了,同名函数一律按覆盖处理。看起来没有问题,也很方便的样子。 其实,对于复杂的情况下,这种做法是相当不方便的。举个栗子,当一个基类派生出各种不同的子类,每个子类都有这么一个同名函数,你现在去调用这个各个子类中的同名函数,然后手敲各个实例名来调用?对于复杂的调用,C++还是得靠指针,这样也更高大上一些。
直接来看一个代码实例:(来自谭老师的红皮书)
#include<iostream>
#include<string>
using namespace std;
class Student {
public:
Student(int, string, float);
void display();
protected:
int num;
string name;
float score;
};
Student::Student(int n, string nam, float s) {
num = n;
name = nam;
score = s;
}
void Student::display() {
cout << "num:" << num << "\nname:" << name << "\nscore:" << score << "\n\n";
}
class Graduate :public Student {
public:
Graduate(int, string, float, float);
void display();
private:
float pay;
};
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){}
int main() {
Student stud1(1001, "Li", 87.5);
Graduate grad1(2001, "Wang", 98.5, 563.5);
Student *pt = &stud1;
pt->display();
pt = &grad1;
pt->display();
grad1.display();
system("pause");
return 0;
}
输出:
如上述代码所示,我用基类指针pt指向基类实例stud1,再通过pt调用stud1的display函数,是完全没有问题的。但是,如果用基类指针指向子类实例grad1,再调用其display函数,发现陈列的仍然基类的信息,并没有访问grad1的第4个信息pay。我们可以看到,如果用Graduate的某个实例名grad1调用display函数,可以完全实现新的display。当然,我们想要的并不是通过实例名调用,而是通过基类指针调用。那么,就需要我们的虚函数了。
如下:将基类中的display函数前面加一个virtual就可以了
virtual void display();
再看一下输出结果:
关键是第二段输出,基类指针访问子类实例中的同名函数,能够实现子类函数的功能。这就是虚函数的作用之所在!
总结
当把基类的某个成员函数声明为虚函数后,允许在其派生类中对该函数重新定义,赋予它新的功能,并且可以通过指向基类的指针指向同一类族中不同类的对象,从而调用其中的同名函数。由虚函数实现的动态多态性就是:同一类族中不同类的对象,对同一函数调用做出不同的响应。