我有一个使用另一个DLL文件的DLL文件的EXE文件。出现了这种情况:
在DLL文件1中:
class abc
{
static bool FindSubFolders(const std::string & sFolderToCheck,
std::vector< std::string > & vecSubFoldersFound);
}
在DLL文件2中:
void aFunction()
{
std::vector<std::string> folders;
std::string sLocation;
...
abc::FindSubFolders(sLocation, folders)
}
在发布模式下,一切正常。但是在调试模式下,我在文件夹向量中的std::strings
之一的析构函数中出现断言失败(当文件夹在aFunction结束时超出范围时):
dbgheap.c : line 1274
/*
* If this ASSERT fails, a bad pointer has been passed in. It may be
* totally bogus, or it may have been allocated from another heap.
* The pointer MUST come from the 'local' heap.
*/
_ASSERTE(_CrtIsValidHeapPointer(pUserData));
我认为这是因为内存已经在DLL文件1的堆上分配,但是在DLL文件2中被释放。
dbgheap.c
中的评论似乎非常坚持认为这是一个问题。
为什么这是一个问题,如果我忽略它似乎工作正常?是否有一种非断言失败的方式呢?
答案 0 :(得分:11)
正如肖恩已经说过的那样,发布版本只是忽略了删除语句,所以你所希望的最好是内存泄漏。
如果您可以控制两个DLL文件的编译方式,请确保使用运行时库的多线程调试DLL(/ MDd)或多线程DLL(/ MD)设置。这样,两个DLL文件将使用相同的运行时系统并共享相同的堆。
缺点是您需要将运行时系统与您的应用程序一起安装(Microsoft提供了一个安装程序)。它可以在您的开发计算机上正常工作,因为Visual Studio也安装了该运行时系统,但在新安装的计算机上,它将报告丢失的DLL文件。
答案 1 :(得分:6)
最有可能的是,发布版本具有相同的问题,但发布版本没有断言。他们只是忽略了这个问题。你可能永远不会看到问题。或者您可能会看到数据损坏。或者你可能会看到崩溃。也许只有你的用户会遇到你根本无法重现的错误。
不要忽略CRT断言。
您应该始终使用适当的解除分配器(与开始时使用的分配器匹配的解除分配器)。如果您在DLL文件中使用静态CRT库,则DLL文件使用不同的堆。你不能在整个堆中释放内存。使用相同的堆分配和释放一块内存。
如果您在DLL文件中使用共享CRT库,那么它们应该使用相同的堆,您可以在一个DLL文件中分配并在另一个DLL文件中取消分配。
答案 2 :(得分:6)
正如其他人所说,可以通过确保两个模块之间共享CRT来解决问题。但是有一些常见的情况是这份合同很难执行。
原因是如果EXE和DLL没有链接到相同的CRT 版本(如6.0,7.0,8.0),确保链接共享CRT将不起作用。例如,如果您使用已在VC6.0中构建的DLL并尝试在VS2010中使用EXE版本,则会遇到与以前相同的问题。 CRT的两个版本将在您的进程中加载,并且每个版本都使用自己的堆进行分配,无论您的EXE和DLL使用“共享”CRT,它们都不会相同。
API的一个更好的做法是确保在一侧分配的对象也在同一侧被销毁。我知道这听起来很难看,但这是确保DLL保持二进制兼容的唯一方法。
答案 3 :(得分:5)
如果应用程序或一个(或多个)DLL文件与标准库的静态版本链接,则这只是一个问题。大约十年前,MS发布了标准库的共享库版本。这是因为标准库的每个版本都将构建自己的内部堆,因此需要使用多个堆,您必须将内存释放到正确的堆中。通过使用标准库的共享版本,它们都使用相同的堆。
现在是应用程序的标准做法,应该构建所有DLL文件以使用标准库的动态版本。
上述唯一的警告是,如果您创建自己的堆并从此堆分配内存。但这是一个非常专业的程序,只在极少数情况下才能完成(如果你理解足够使用它,那么你就不会出现这个问题)。