我有一些在大型系统中崩溃的代码。但是,代码本质上归结为以下伪代码。我已经删除了很多细节,因为我试图将其归结为裸露的骨头;我不认为这会遗漏任何重要的东西。
// in a DLL:
#ifdef _DLL
#define DLLEXP __declspec(dllexport)
#else
#define DLLEXP __declspec(dllimport)
#endif
class DLLEXP MyClass // base class; virtual
{
public:
MyClass() {};
virtual ~MyClass() {};
some_method () = 0; // pure virtual
// no member data
};
class DLLEXP MyClassImp : public MyClass
{
public:
MyClassImp( some_parameters )
{
// some assignments...
}
virtual ~MyClassImp() {};
private:
// some member data...
};
和:
// in the EXE:
MyClassImp* myObj = new MyClassImp ( some_arguments ); // scalar new
// ... and literally next (as part of my cutting-down)...
delete myObj; // scalar delete
请注意,正在使用匹配的标量 new 和标量 delete。
在 Visual Studio (2008 Pro) 的调试版本中,在 Microsoft 的 <dbgheap.c> 中,以下断言失败:
_ASSERTE(_CrtIsValidHeapPointer(pUserData));
靠近堆栈顶部的是以下项目:
mydll_d.dll!operator delete()
mydll_d.dll!MyClassImp::`vector deleting destructor'()
我认为这应该是
mydll_d.dll!MyClassImp::`scalar deleting destructor'()
也就是说,程序的行为就像我写的一样
MyClassImp* myObj = new MyClassImp ( some_arguments );
delete[] newObj; // array delete
中的地址pUserData
是它myObj
自己的地址(而不是成员)。该地址周围的内存如下所示:
... FD FD FD FD
(address here)
VV VV VV VV MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
FD FD FD FD AB AB AB AB AB AB AB AB EE FE EE FE
...
其中四个VV
s 可能是虚函数表的地址,MM...MM
是可识别的成员数据,其他字节是调试器放置的各种特殊标记(例如,FD FD
s 是对象存储周围的“保护字节”)。
在断言失败前不久,我确实看到了VV
s 的变化,并想知道这是否是由于切换到基类的虚函数表所致。
我知道类层次结构中错误级别的问题正在被破坏。这不是这里的问题。我的析构函数都是虚拟的。
我注意到微软的页面“BUG: Wrong Operator Delete Called for Exported Class” http://support.microsoft.com/kb/122675 但这似乎是关于错误的可执行文件(带有错误的堆)试图承担破坏的责任数据。
就我而言,删除析构函数的错误“风格”似乎正在被应用:即向量而不是标量。
我正在尝试生成仍然存在问题的最小缩减代码。
但是,任何有助于如何进一步调查此问题的提示或技巧将不胜感激。
也许这里最大的线索是mydll_d.dll!operator delete()
堆栈上的。我应该期望这是myexe_d.exe!operator delete()
,表明DLLEXP
s 已经“丢失”了吗?
我想这可能是双重删除的一个实例(但我不这么认为)。
关于_CrtIsValidHeapPointer
检查的内容,我可以阅读一个很好的参考吗?