加载 .dll 时,实例指针与 vfptr 不同
Posted
技术标签:
【中文标题】加载 .dll 时,实例指针与 vfptr 不同【英文标题】:Instance pointer differs from vfptr when .dll is loaded 【发布时间】:2017-11-09 09:34:49 【问题描述】:所以,我在使用 Visual Studio 的测试套件时发现了一些有趣的东西:
我有一个 A 类的实例,地址是 0x0656a64c
。
然后当我查看变量时,它说它的__vfptr
指向0x077e7c0c
。
据我所知,一个类的虚拟表指针应该位于类实例的前 4 个字节(或 64 位应用程序上的 8 个字节),除非是多重继承的情况(那么它只是偏移量)第一个 vtable 地址)。
我观察到 Visual Studio 将我的测试编译成 .dll 并运行其测试工具动态加载 .dll。
这可能是导致地址差异的原因吗?
这是 VS 调试器的截图
【问题讨论】:
【参考方案1】:您混淆了 vptr 的地址和 vptr 指向的地址。您(大致)正确的是,通常的实现是 vptr 是对象中的第一件事 - 但是 Visual Studio 向您显示 vtable 的地址(在类的所有对象之间共享) )。
【讨论】:
啊,是的,我肯定误解了这些价值观。谢谢你的澄清【参考方案2】:这只是 MS C++ 编译器的正常行为。 2 个创建相同类对象实例的 DLL 将具有不同的 vtable。直觉会说 vtable 应该与所有实例共享,无论 DLL 的代码如何创建(新)它。它应该对类的实现是通用的。不正确。
这会在动态释放 DLL 时产生危险。所有分配的对象可能仍处于有效(进程)堆中。但是,如果它有一个虚拟析构函数,那么它是在 vtable 中定义的。它现在处于释放空间中。因此 vtable 已损坏。任何使用它都会产生异常。
解决方案是在释放 DLL 之前清理由动态 DLL 进行的任何/所有分配,这些分配可能也有一个 vtable(例如虚拟析构函数)。在进程堆中进行的任何不指向基于 DLL 的 vtable 的分配都可以。如果没有正确跟踪,最坏的情况是内存泄漏。
【讨论】:
以上是关于加载 .dll 时,实例指针与 vfptr 不同的主要内容,如果未能解决你的问题,请参考以下文章
虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)