在VS2013编程,调试
问题 : 菱形继承会引来,二义性
1.源代码
</pre><pre name="code" class="cpp">#include <iostream> using namespace std; class Base { public: virtual void FunTest() { cout << "Base::FunTest () " << endl; } virtual void FunTest1() { cout << "Base::FunTest1 () " << endl; } }; class C1 :virtual public Base { public: virtual void FunTest2() { cout << "C1::FunTest2 () " << endl; } }; class C2 :virtual public Base { public: virtual void FunTest3() { cout << "C2::FunTest3 () " << endl; } }; class Der : public C1, public C2 { public: virtual void FunTest4() { cout << "Der::FunTest4 () " << endl; } virtual void FunTest5() { cout << "Der::FunTest5 () " << endl; } virtual void FunTest6() { cout << "Der::FunTest6 () " << endl; } }; typedef void(*vftab) (); void Test() { Der d; cout << sizeof(d) << endl; cout << "-------C1---- " << endl; int *vfpt = (int *)(*(int *)&d); vftab vft = (vftab)(*(int *)vfpt); while (vft != 0) { vft(); vfpt++; vft = (vftab)(*vfpt); } cout << endl; cout << "-------C2---- " << endl; vfpt = (int *)(*((int *)&d + 2)); vft = (vftab)(*(int *)vfpt); while (vft != 0) { vft(); vfpt++; vft = (vftab)(*vfpt); } cout << endl; cout << "-------Base---- " << endl; vfpt = (int *)(*((int *)&d + 4)); vft = (vftab)(*(int *)vfpt); while (vft != 0) { vft(); vfpt++; vft = (vftab)(*vfpt); } } int main() { Test(); getchar(); return 0; }
运行结果:
这些结果是怎么来的哪?为什么 d的大小为20个字节??
接下来就看看d的内存吧
现在知道了为什么d的大小为20个字节了吧!但是问题有来了,d的内存中存的都是些什么东西哪?
一步一步看吧!
貌似这些都是地址,那就看看这些地址有存了些什么
一. 看一下到 地址(0x 00 2a dd 2c)
可以从监视1看到 地址(0x 00 2a 11 59)是虚函数C1::FunTest2()的入口地址
可以从监视1看到 地址(0x 00 2a 12 53)是虚函数Der::FunTest4()的入口地址
可以从监视1看到 地址(0x 00 2a 13 de)是虚函数Der::FunTest5()的入口地址
可以从监视1看到 地址(0x 00 2a 10 05)是虚函数Der::FunTest6()的入口地址
总结1:地址(0x 00 2a dd 2c),应该是类Der从类C1继承下来虚表的地址,虚表中存的是类Der和类C1的虚函数的地址
二. 看一下到 地址(0x 00 2a dd 5c)
可以看到 0x 00 4e f9 30 + 0x 00 00 00 0c = 0x 00 4e f9 3c
总结2:地址(0x 00 2a dd 5c)下,应该是存的是偏移
三. 看一下到 地址(0x 00 2a dd 44)
可以从监视1看到 地址(0x 00 2a 10 69)是虚函数C2::FunTest3()的入口地址
总结3:地址(0x 00 2a dd 44),应该是类Der从类C2继承下来虚表的地址,虚表中存的是类C2的虚函数的地址
四. 看一下到 地址(0x 00 2a dd 68)
可以看到 0x 00 4e f9 38 + 0x 00 00 00 04 = 0x 00 4e f9 3c
总结4:地址(0x 00 2a dd 68)下,应该是存的是偏移
五.看一下到 地址(0x 00 2a dd 50)
可以从监视1看到 地址(0x 00 2a 12 c6) 是虚函数Base::FunTest()的入口地址
可以从监视1看到 地址(0x 00 2a 12 7b) 是虚函数Base::FunTest1()的入口地址
总结5:地址(0x 00 2a dd 50),应该是类Der从类Base继承下来虚表的地址,虚表中存的是类Base的虚函数的地址
根据上面的一步一步的分析 :可以得到菱形虚拟继承(含有虚函数,但没有被重写)的模型:
看看这两个的偏移,就是它们保证了类 Der在继承 类Base的虚拟函数,以及类Base的数据成员的唯一性,从而避免了二义性的产生。
注意:在函数Test()中的地址偏移,是为了方便从内存中取得地址,查看里面是什么内容,如果每个类加上自己的数据成员,那就不是那样取值了,
我说的是什么哪 ?意思就是说 :
vfpt = (int *)(*((int *)&d + 2));
vfpt = (int *)(*((int *)&d + 4));
看见 2 和 4 了吧,说的就是这个