当C++多继承遇上类型转换(2)

对于多继承下的内存布局问题,请参考: 。其实这里的问题也是与内存不就息息相关,也算是对前面这篇博文的一点补充。前面博文指出了使用同一对象调用不同的函数时,在被调用函数内部的this指针是不同的,以及为什么不同然而没有说明这里的this是何时被确定的,是编译时?还是运行时?

还是先来看看前面代码的内存布局。

当C++多继承遇上类型转换

之所以会出现pI1和pI2指向了同一个地址,是因为C++编译器没有足够的知识来把IA*类型转换为IB*类型,只能按照传统的C指针强制转换处理,也就是指针位置不变。为了验证上面的结论,简单的把pIA和pIB打印出来即可。把main()函数修改为如下:

int main(int argc, char** argv)
{
 I1* pI1 = CreateC();
 pI1->vf1();

I2* pI2 = (I2*)pI1;
 pI2->vf2();

cout << "pI1指向的地址为:"<<std::hex << pI1 << endl;
 cout << "pI2指向的地址为:"<<std::hex << pI2 << endl;
 delete pI1;
 return 0;
}

执行结果为:

当C++多继承遇上类型转换

可见pI1和pI2确实指向了同一个地址,而这个地址就是I1类的虚表。由于虚函数是按照顺序定位的,编译器编译pI2->vf2()的时候,不管实际的pI2指向哪里,都把它当做指向了I2的虚表,根据I2类定义,推出I2::vf2()这个函数位于其虚表的第0个位置,所以就直接把pI2指向的地址作为vf2来调用。而实际上,这个位置恰恰是I1虚表的第0个位置,也就是I1::vf1的位置,所以实际执行时调用的是I1::vf1()。其实这种情况是有些特殊的,也就是这个位置正好也是一个函数地址,而且函数原型也一样,要是有任何不同的地方,就会造成调用失败,反而更容易及时的提醒开发者。如下代码所示:

#include <iostream>
#include <hash_map>
using namespace std;


class I1
{
public:
 virtual void vf1()
 {
  cout << "I'm I1:vf1()" << endl;
 }
};


class I2
{
public:
 virtual void vf2()
 {
  cout << "I'm I2:vf2()" << endl;
 }
 virtual void vf3()
 {
  cout << "I'm I2:vf3()" << endl;
 }
};


class C : public I1, public I2
{
private:
 hash_map<string, string> m_cache;
};


I1* CreateC()
{
 return new C();
}


int main(int argc, char** argv)
{
 I1* pI1 = CreateC();
 pI1->vf1();


 I2* pI2 = (I2*)pI1;
 pI2->vf2();
 pI2->vf3();


 cout << "pI1指向的地址为:"<<std::hex << pI1 << endl;
 cout << "pI2指向的地址为:"<<std::hex << pI2 << endl;
 delete pI1;
 return 0;
}

此时的内存布局为:

当C++多继承遇上类型转换

在执行pI2->vf2()时,执行的是I1::vf1(),但是不会报错。当执行pI2->vf3();时,由于调用的地址并不是一个函数指针,所以会报错。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/c9bbe28d55a25caa610f963f94cd0e22.html