您的位置:首页 > 编程语言 > C语言/C++

从一道面试题分析c++的多态特性

2015-01-21 13:03 267 查看
首先来看一段代码:
#include <iostream>
using namespace std;

class	IHello{
public:
virtual void Hello()= 0;
};

class	IWorld{
public:
virtual void World()= 0;
};

class HelloWorld:public IHello, public IWorld{
public:
virtual void Hello(){
cout<<"Hello"<<endl;
}
virtual void World(){
cout<<"World"<<endl;
}
};

int main(){
IHello* hello = new HelloWorld;
IWorld* world = (IWorld*)(void*)hello;
world->World();
}
这是一个经典的C++多态问题。答案正如你出乎预料的那样,输出Hello。
对此,前辈的解释为“这是面向对象中经常会使用的一种使程序呈现多态性的手段,也就是动态联编,只有运行时才知道具体执行的代码,这里面的子对象HelloWorld在生成对象时会从对象初始地址开始建立一个虚函数表,用来保存虚函数的地址,也即第一个地址指向Hello(),第二个地址保存World(),继承时,HelloWorld对象的虚函数表指针则将相应的指针指向其覆盖的函数,但是虚函数地址的相对位置还是Hello()在前,World()在后,在用IWorld进行强转之后,world指向了HelloWorld的入口虚函数地址,当执行world->World()时实际上是转去执行Hello函数去了。”
对这段话的理解还是不甚清楚。于是写了这样一段代码进行测试。
int main()
{
HelloWorld* aa =new HelloWorld;
IWorld* world1 = (IWorld*)aa;
world1->World();
}
输出为world.按照上面的解释,world1指针执行world()函数时,会从虚函数表中找第一个函数地址,所以执行结果也应该是hello才对。那问题在哪里呢?
于是又写了一段代码来进行测试:
int main(){
HelloWorld* aa =new HelloWorld;
IWorld* world1 = (IWorld*)(void*)aa;
world1->World();
}
这次的执行结果,没错正是hello。
两次的执行结果迥然不同,只因为加了一次void*强转,那么对编译器的影响是什么呢?
不妨看看内存是怎么样的。执行第一段代码时,查看到的内存如下
可以看到world1指针的指并不等于aa,而是向后偏移了4个字节,这样就正好指向了world()函数的地址。
而第二段代码的内存如下
这次两个指针的值是一样的,所以也就产生了上述结果。
于是我们有这样的猜想,当将一个子类的指针强转为父类的指针时,会根据其父类对象在子类对象中的位置产生相应的偏移。而当你强转成void*指针之后,由于无法识别指针的类型,所以就只能直接赋值了

                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: