pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i )

#include <iostream>
using namespace std;

calss A{

virtual void g()
{
cout << "A::g" << endl;
}
private:
virtual void f()
{
cout << "A::f" << endl;
}
};
class B : public A {
void g()
{
cout << "B::g" << endl;
}
virtual void h()
{
cout << "B::h" << endl;
}
};
typedef void( *Fun )( void );

int main()
{
B b;
Fun pFun;
for(int i = 0 ; i < 3; i++)
{
pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i );
pFun();
}
}
输出结果:
B::g
A::f
B::h

typedef void( *Fun )( void ); //是吧Fun定义为一个没有参数,返回void类型的函数指针

*( ( int* ) * ( int* )( &b ) + i )这一段,
(int*)*相当于没有进行任何操作,所以等同于
*( ( int* )( &b ) + i )
这里先取b的地址,然后把地址转换成int*,之后+i是指针算术,也就是在b的地址上加一个int的长度,最后最前面的*是解指针,所以这段最后返回的是“b的地址+i个int长度的值”
最前面的(Fun)是强制把“b的地址+i个int长度的值”转换为一个“没有参数,返回void类型的函数指针”,所以pFun就是一个函数指针,其指向的位置从一开始的b的地址,每次循环加一个int的长度

然后我们开看,b的地址,b是一个B类型,B类型的第一个函数是g(),而第一次循环pFun的地址就是b的地址,b又没有属性(私有或共有变量)所以b的地址就是b中第一个函数g的地址,所以第一次循环的pFun()相当于调用B::g

( Fun )*( ( int* ) * ( int* )( &b ) + i ); 这里*( ( int* ) * ( int* )( &b ) + i )最前面的*是对上面的结果进行解指针,也就取把b的地址+i个int长度的这个地址的值,并把它转换为Fun类型,也就是一个没有参数,返回void类型的函数指针,所以最后得到的就是一个函数指针。这个指针所指向的地址就是

然后我们来看循环,循环中3次pFun变量分别被赋了3次值,每次都是一个函数指针
由于B类型中有virtual函数,所以b的地址指向的是b的vtbl(如果你不知道这个,你面试就没戏了),vtbl可以看作一个保存了函数指针的数组,每个元素就是一个int长度,在vtbl中B::g,A::f,B::h是按照如上顺序排列的,所以第一次循环指向B::g,那么后两次就指向A::f和B::h了

至于为什么是按照这样的顺序排列的,是因为其声明顺序,首先是父类的virtual函数按照其生命顺序放入vtbl中,然后是子类的放进去,所以其顺序是:A::h,A::f(这是父类的声明顺序)子类中只有B::H是新声明的,所以顺序是A::g,A::f,B::h。
又因为b的类型是b,你知道什么事多态和动态绑定,就明白第一个调用的为什么是B::g而不是A::g了。

另外这道面试题对编译器进行了大量假设,估计出题的是个搞windows开发的,没见过别的平台,其实这道题在不同的编译器上得到的结果往往是不同的。 比如在我的64位mac上,这个代码是执行错误,segment fault,因为64位的vtbl每个元素长度是64,是2个int长。

猜你喜欢

转载自blog.csdn.net/qingzhuyuxian/article/details/108096427