阿里关于多态的笔试题,通过这道经典的题目,我们来了解什么是动态绑定与静态绑定
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;
}
问题选项:
A->0
B->1
A->1
B->0
编译出错
以上都不对
记住:virtual 函数是动态绑定,而缺省参数值却是静态绑定。 意思是你可能会 在“调用一个定义于派生类内的virtual函数”的同时,却使用基类为它所指定的缺省参数值。
结论:绝不重新定义继承而来的缺省参数值!(可参考《Effective C++》条款37)
对于本例:
B*p = newB;
p->test();
p->test()执行过程理解:
(1) 由于B类中没有覆盖(重写)基类中的虚函数test(),因此会调用基类A中的test();
(2) A中test()函数中继续调用虚函数 fun(),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->”;
(3) 缺省参数值是静态绑定,即此时val的值使用的是基类A中的缺省参数值,其值在编译阶段已经绑定,值为1,所以输出“1”;
最终输出“B->1”。所以大家还是记住上述结论:绝不重新定义继承而来的缺省参数值!、
如果还不是很理解那我们再通过例子,来看
#include <iostream>
using namespace std;
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[])
{
A*p1 = new A; // 父类指针指向父类
A*p2 = new B; // 父类指针指向子类
B*p3 = (B*)new A; // 子类指针指向父类
B*p4 = new B; // 子类指针指向子类
//测试test()
p1->test(); // A -> 1
p2->test(); // B -> 1
p3->test(); // A -> 1
p4->test(); // B -> 1
// 测试func()
p1->func(); // A -> 1
p2->func(); // B -> 1
p3->func(); // A -> 0
p4->func(); // B -> 0
system("pause");
return 0;
}
测试test()函数的时候。因为缺省参数在编译的时候都已经确定好了,都是填上基类(父类)的缺省值。所以val值都是 1
测试func()函数的时候。(p1,p2好说,先说p1,p2)
- p1是自己指向自己 ,不构成多态,用自己的构造和缺省值,所以输出 A-> 1。
- p4是自己指向自己,不构成多态,用自己的构造和缺省值,所以输出 B -> 0。
- p2是父类指向子类,所以缺省值是父类的(所以是1),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->”
- p3是子类指向父类,所以缺省值是子类的(所以是0),虚函数是动态绑定的,new A指向的是A, 所以会输出 A->。所以是A->0。