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

首先来看一段代码:

#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*指针之后,由于无法识别指针的类型,所以就只能直接赋值了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值