C++——一道关于多态的经典面试题
这个试题的问题是以下程序输出结果是什么?
A: A->0 B: B->1 C: A->1 D: B->0 E: 编译出错 F: 以上都不正确
class A
{
public:
virtual void func(int val = 1){ std::cout<<"A->"<< val <<std::endl;}
virtual void test(){ func();}
};
class B : public A
{
public:
void func(int val = 0){ std::cout<<"B->"<< val <<std::endl; }
};
int main(int argc ,char* argv[])
{
B*p = new B;
p->test();
return 0;
}
首先这道题的第一个坑在于,可能有的人看一遍代码,可能会觉得p是B类型的一个指针,指向的也是一个B类型的对象,可是B类中并没有test函数,所以答案是E。但是B类继承了A类,虽然在A类中test函数是一个虚函数,并且在B类中并没有被重写,但是test函数同样通过虚函数表的形式继承给了B类,所以E选项是错误的。
这道题的第二个坑在与B类中的func函数没有加virtual修饰,因此会有些人认为这个func函数就不是一个虚函数,因此在p调用test函数时,test函数所调用的func函数就是A类中的func函数,所以输出为A->1,因此选C。但是虽然B类中的func函数并没有被virtual修饰,但是在虚函数的定义中我们可以知道,派生类中的虚函数可以不用virtual修饰,虽然这是一种不规范的写法,但是派生类在从基类继承这个函数的时候,就已经把这个函数的虚函数性质一并继承下来了,也就是说虽然这B类中的func函数并没有用virtual修饰,但是A类中的func函数已经被virtual修饰了,所以B类中的func函数也具有虚函数的性质。所以C选项也是错误的。
这道题的第三个坑就在第二坑分析正确的基础上,经过第二点的分析,我们知道了B类的func函数也是一个虚函数,并且B类重写了A类的func函数,所以在B类的虚函数表中B类自己的func函数的地址已经对继承下来的虚函数表里原来的func函数地址进行了覆盖,因此在调用func函数的时候,调用的是自己的实现的func函数,因此输出的结果是B->0,选D。但是这样就掉到这道题的最后一个坑里面了,因为虚函数完成重写以后,在调用时,相当于是把派生类的虚函数重写以后的函数体覆盖到基类的虚函数上,当基类的虚函数的参数列表有缺省值时,并不会对参数列表进行修改,因此这里test函数调用的func函数的本质是
virtual void func(int val = 1)
{
std::cout<<"B->"<< val <<std::endl;
}
输出的结果是B->1,答案选B。
所以从这道题里我们可以得出一个结论:如果虚函数的参数列表中设置有缺省值,那么我们的派生类在重写这个虚函数时不要对这个缺省值进行修改,因为修改以后在调用时并不会把修改后的缺省值应用到参数上。