一些常见的笔试题(三)

先来看一段简单的代码。

 

 

这道题也是经常在笔试中会遇到的,很多人可能都不能完全答对,一般只要c++有一定的经验,大部分都能答对前面两个函数调用,对于剩下两个可能就不太确定了,虽然感觉不太实用,但这的的确确能体现一个程序员对类继承的理解深度。

有些人的可能答案是这样的:

CDerived::func1

CBase::func2

CBase::func1

CBase::func2

但结果真是这样的吗?

首先我们分析一下上面的结果。

① 函数调用pBase->func1()的结果显然是CDerived::func1。会c++的都知道,这是多态的表现,基类的指针指向派生类对象,如果该函数是虚函数,那会调用会调用当前实际对象的vtable表中对应的函数,也就是CDerived::func1。

② 函数调用pBase->func2()的结果是CBase::func2。原因在于:如果被调用的函数不是虚函数,那么函数调用会根据指针的类型来调用,而这里是基类指针在调用,所以最终调用的是基类的函数func2() ,输出为CBase::func2。

 

对于这句话,CDerived* pDerived = (CDerived*)&base,我原先的想法是,这肯定有问题,怎么能直接将基类对象的指针强转成派生类的指针呢,这样肯定会引起对象的切割。但实际上,这和对象切割一点关系都没有,如果是(CDerived)base,将对象base强转成CDerived的对象,这才叫对象切割。所以,这里直接进行指针转化是没有问题的。不就将指针的类型转换一下嘛,又没有改变指针本身的值,所以这里没有危险。

③ 对于函数调用pDerived->func1()来说,因为之前进行了指针的转换,将基类的对象base的地址赋值给派生类的指针,所以很多人就不确定了,到底是调用哪个类的函数呢,是CBase::func1还是CDerived::func1,结果往往是连懵带猜,随便写个算了。其实如果再对vtable稍微深想一下的话,可能就知道应该是什么样的答案了。

我的想法是,无论是pBase->func1()还是pDerived->func1(),它们的做法都是需要先判断函数fun1是不是虚函数,如果是的话,那会就会去vtable里面去找,当前对象应该调用的那个函数。每个类在声明一个或多个对象之后,如果类中有虚函数,那么就会为每个对象镶嵌一个__vfptr的指针,用它来指向vtable表,并且同一个类的所有对象的__vfptr值和vtable表中的内容都是完全一样的【但是,我有个疑问,因为同一个类的所有对象共享一个__vfptr指针,到底__vfptr能不能看成类的静态成员?这个估计与编译器实现有关】。所以,对于pDerived->func1()来说,它会去pDerived所指向对象的的vtable表中查找一下fun1这个函数,而pDerived所指向对象是base,也就是基类的对象,因此,找到的函数就是CBase::func1,所以,答案是:CBase::func1。

④ 如果能理解②和③的话,对于函数调用pDerived->func2()来说,答案就已经很明确了,就是CDerived::func2了。因为fun2不是虚函数,所以函数调用的时候不会去vtable中去找,也就是说,fun2的调用不能根据对象的实际类型来找对应的函数,而只能根据调用的指针的类型来判断调用的函数。

所以,最终的结果是:

CDerived::func1

CBase::func2

CBase::func1

CDerived::func2

 

因为我对编译原理不怎么了解,没学过,有些东西可能还是没说明白甚至是说错了,如有问题,谢谢纠正。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值