C++ 为什么向量删除析构函数作为标量删除的结果被调用?

C++ 为什么向量删除析构函数作为标量删除的结果被调用?,c++,debugging,visual-c++,msvcrt,destructor,C++,Debugging,Visual C++,Msvcrt,Destructor,我有一些代码在一个大系统中崩溃了。 然而,代码本质上归结为以下伪代码。 我已经删除了很多细节,因为我试图把它归结为赤裸裸的骨骼; 我不认为这遗漏了任何重要的东西 // in a DLL: #ifdef _DLL #define DLLEXP __declspec(dllexport) #else #define DLLEXP __declspec(dllimport) #endif class DLLEXP MyClass // base class; virtual { public:

我有一些代码在一个大系统中崩溃了。 然而,代码本质上归结为以下伪代码。 我已经删除了很多细节,因为我试图把它归结为赤裸裸的骨骼; 我不认为这遗漏了任何重要的东西

// 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...
};
以及:

请注意,正在使用匹配的标量new和标量delete

在Visual Studio(2008 Pro)中的调试构建中, 在微软的, 以下断言失败:

_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
s是对象存储器周围的“保护字节”)

在断言失败前不久,我确实看到了
VV
s的变化, 不知道这是否是由于切换到基类的虚拟函数表

我知道类层次结构中的错误级别正在被破坏。 这不是问题所在;我的析构函数都是虚拟的

我注意到微软的网页 “错误:为导出的类调用了错误的运算符Delete” 但这似乎是由于错误的可执行文件(具有错误的堆)试图承担数据破坏的责任

在我的例子中,删除析构函数的错误“味道”似乎被应用了: i、 向量而不是标量

我正在尝试生成仍然存在问题的最小精简代码

然而,任何提示或提示,以帮助如何进一步调查这个问题将不胜感激

也许这里最大的线索是
mydll\u d.dll!堆栈上的运算符delete()
。 我应该期望这是
myexe\u d.exe!运算符delete()
, 表示
DLLEXP
s已“丢失”

我想这可能是双重删除的一个例子(但我不这么认为)


关于
\u CrtIsValidHeapPointer
检查的内容,我能读到什么好的参考资料吗?

微软为他们的C运行时提供了源代码;您可以在那里查看
\u CrtIsValidHeapPointer
的功能。在我的安装中,它位于
C:\ProgramFiles(x86)\Microsoft Visual Studio 10.0\VC\crt\src\dbgheap.C

另一个建议是检查发动机的拆卸情况

delete newObj; // scalar delete
并将其与为生成的反汇编进行比较

delete[] newObj;

测试您关于调用
delete[]
的理论。类似地,您可以检查调用堆栈中的

delete pointerToClassLikeMyClassThatIsInExeAndNotDll;

来测试你关于
mydll\u d.dll的理论!运算符delete()
myexe\u d.exe!操作符delete()

听起来这可能是一个从一个堆分配到另一个堆的问题。当从dll分配对象时,这可能是一个问题,因为dll有自己的堆。从你展示的代码来看,这似乎不是问题所在,但可能在简化过程中丢失了一些东西?在过去,我看到过这样的代码使用工厂函数和对象上的虚拟销毁方法来确保在dll代码中进行分配和删除。

感谢您提供的所有答案和评论。 所有这些都是有用和相关的

欢迎提供更多信息


以下是Hans Passant对我的问题的评论:


一旦开始从DLL导出类, 使用/MD编译变得非常重要。 在我看来像是/MT

因此,我仔细研究了整个项目中的链接设置。 我发现了一个/MT和/MTd的“隐藏”实例,它应该是/MD和/MDd, 加上其他设置中的一些相关不一致

纠正了这些错误, 现在不会抛出任何断言,并且代码似乎运行正常


以下是在执行时遇到崩溃或断言失败时需要检查的一些事项:调用作用域和析构函数。 确保在所有项目中(包括依赖项) 在所有配置中(尤其是在有问题的配置中):

(此处*.vcproj路径相对于。)

  • 在中选择了正确的运行时 C/C++|代码生成|运行时库 ;
  • 适当的定义(如有)在 C/C++|预处理器|预处理器定义 特别是关于静态库和动态库的使用 (例如,STLP使用静态库与STLport使用动态库的对比)
  • 在中选择适当版本的库 链接器|输入|其他依赖项 特别是关于静态运行库与DLL的“包装器” (例如stlport_static.lib与stlport.N.M.lib)

有趣的是,我所期望的delete的标量“味道”似乎仍然没有被调用(断点从未被命中)。 也就是说,我仍然只看到向量删除析构函数。 因此,这可能是一条“红鲱鱼”

也许这只是微软的一个实施问题,
或者,我还遗漏了其他一些微妙之处。

这种行为对于MSVC 9来说是特殊的,在MSVC 9中,隐式生成了一个导出类的delete运算符,该类有一个虚拟析构函数,并将其修改为向量dtor,其中1表示(标量),3表示(向量)

这件事的真正问题是,它打破了new/delete的标准形式,客户机编码人员无法在其代码中禁用vector delete操作符,如果他认为使用它是个坏主意的话
delete pointerToClassLikeMyClassThatIsInExeAndNotDll;
delete pointerToClassLikeMyClassThatIsInExeAndNotDll;