【牛客网】---多态经典选择题

【牛客网】 多态经典选择题

  • 引言:今天在牛客网刷题时遇到了三个很经典的关于继承和多态的选择题,想在这里和大家一起分享一下思路和心得体会。

在这里插入图片描述

  • :在这里我们注意到virtual函数是动态绑定,而C++编译器中默认缺省参数值则是静态绑定。
    那么虚函数是如何实现动态绑定的呢?
    :通过类的虚函数表和对象的虚表指针,通过对象的虚表指针我们可以找到实际类的虚函数表,从而多态调用不同的虚函数。
    根据《Effective C++》条款37结论:绝不重新定义继承而来的缺省参数值

接下来我们需要理解题中p–>test()所执行的过程:

  • (1) 由于B类中没有覆盖(重写)基类函数中的虚函数tese(),因此会调用基类A中的test();
  • (2) A中test()函数中继续调用虚函数
    fun(),因为虚函数执行动态绑定,p此时的动态类型(即目前所指对象的类型)为B*,因此此时调用虚函数fun()时,执行的是B类中的fun();所以先输出“B->”;
  • (3)缺省参数值是静态绑定,即此时val的值使用的是基类A中的缺省参数值,其值在编译阶段已经绑定,值为1,所以输出“1”;
  • (4)最终输出“B->1”。所以大家还是记住上述结论:绝不重新定义继承而来的缺省参数值!

所以本题选择B:B->1

在这里插入图片描述

  • (1)首先声明为A类型的指针指向实际类型为A的对象,调用的肯定是A的方法,输出1 2,

  • (2)然后声明为A类型的指针指向实际类型为B的对象,则非虚函数调用A的方法,输出1,虚函数调用实际类型B的方法,输出4

  • (3)声明类型为A的指针指向实际类型为B的对象,进行一个强制类型转换,其实这种父类指针指向子类会自动进行类型转换,所以是否强制类型转换都不影响结构,原理同上一步,结果输出1 4

    所以最终输出为121414
    

要点

  • 1.判断要点:如果不是重载也不是覆盖,派生类和基类中一旦出现同名函数,一定触发隐藏机制(这是个简便判断技巧,你可以考虑除去重载和覆盖的任何同名函数情况,一定满足隐藏机制触发的两条规则)。
  • 2.隐藏触发的结果:指针对成员的函数调用取决于指针类型。若本身是基类指针(不管指向基类还是派生类)则仍然调用基类成员(不会牵扯到派生类,此处是隐藏,和多态没关系,按第1点已说明隐藏的触发可以首先排除覆盖,也就是多态问题);
  • 3.若本身是派生类指针,这时你就会看到隐藏的威力!此时不是简单地继承基类的成员,然后根据参数匹配调用,而是隐藏基类成员,只会调用派生类成员。

在这里插入图片描述

重点:C++多态的实现方式是虚函数表和虚表指针。

其中,C++编译器为每个类内含有virtual函数的类生成一个虚函数表,基类生成基类的虚函数表,虚函数表中包含类内所有虚函数的函数指针,即函数指针数组。

C++调用虚函数时通过虚函数指针来间接调用。动态绑定有点类似于函数指针,当函数指针赋予不同值时就调用不同的函数。虚函数指针则是当赋予不同值时调用不同的函数组。

C++编译器为每个含virtual的类对象添加一个虚函数表指针,在调用对象的构造函数的时候初始化该虚函数表指针。在构造子类的时候需要先构造基类,所以在初始化虚函数表的指针时先用基类的虚函数表地址再用子类的虚函数表地址初始化该虚函数指针。
在本题中,在构造基类的时候,虚表指针是指向基类的虚函数表,所以调用的就基类的func();
构造子类的时候,虚表指针则指向了子类的虚函数表,调用的自然就是子类的func()。

本题思路如下:

  • 因为是无参构造,所以默认调用父类的无参构造,输出0,其实父类在构造时不考虑自己是否调用虚函数,因为虚函数还没构建,打印自己的0,然后b构建,这时候已经有A了,调用了子类的虚函数,已经可以指向子类的了,所以打印1,再次调用test,因为指针是B型的,所以调用b的那个,输出2,如果转换成A的实例就不一样了,但是这题没转。

思路如图
在这里插入图片描述

发布了45 篇原创文章 · 获赞 271 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/L19002S/article/details/103132650