【算法】多态经典题目(动态绑定与静态联编)

阿里关于多态的笔试题,通过这道经典的题目,我们来了解什么是动态绑定与静态绑定

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)

  1. p1是自己指向自己 ,不构成多态,用自己的构造和缺省值,所以输出 A-> 1。
  2. p4是自己指向自己,不构成多态,用自己的构造和缺省值,所以输出 B -> 0。
  3. p2是父类指向子类,所以缺省值是父类的(所以是1),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->
  4. p3是子类指向父类,所以缺省值是子类的(所以是0),虚函数是动态绑定的,new A指向的是A, 所以会输出 A->。所以是A->0。

猜你喜欢

转载自blog.csdn.net/weixin_43939593/article/details/105499277