我们有一个主机应用程序和一个DLL,它们都是用相同的编译器设置构建的,都是使用静态CRT /MT
构建的。(用VS2019和VS2022测试)
最后,我们传递一些包含两个STL对象的简单结构,例如
struct MyData
{
std::vector<std::string> entries;
};
...
MyData DLL::getMyData() const
{
// ...
return MyData();
}
当我们在主机应用程序端释放通过DLL的API检索到的MyData
时,获取CRT堆断言。
Debug Assertion Failed!
File: minkernel\crts\ucrt\src\appcrt\heap\debug_heap.cpp
Line: 996
Expression: __acrt_first_block == header
host.exe!free_dbg_nolock(void * const block, const int block_use) Line 996 C++
host.exe!_free_dbg(void * block, int block_use) Line 1030 C++
host.exe!operator delete(void * block) Line 38 C++
host.exe!operator delete(void * block, unsigned __int64 __formal) Line 32 C++
host.exe!std::_Deallocate<16,0>(void * _Ptr, unsigned __int64 _Bytes) Line 255 C++
host.exe!std::_Default_allocator_traits<std::allocator<std::_Container_proxy>>::deallocate(std::allocator<std::_Container_proxy> & _Al, std::_Container_proxy * const _Ptr, const unsigned __int64 _Count) Line 670 C++
host.exe!std::_Deallocate_plain<std::allocator<std::_Container_proxy>>(std::allocator<std::_Container_proxy> & _Al, std::_Container_proxy * const _Ptr) Line 976 C++
host.exe!std::_Delete_plain_internal<std::allocator<std::_Container_proxy>>(std::allocator<std::_Container_proxy> & _Al, std::_Container_proxy * const _Ptr) Line 989 C++
host.exe!std::string::~basic_string<char,std::char_traits<char>,std::allocator<char>>() Line 3007 C++
host.exe!DLL::MyData::~MyData() C++
奇怪的是,MSVC中的地址清除器(以及使用clang/Xcode的ASAN )没有报告错误,而且应用程序在发布版本中似乎是稳定的,但是在MSVC中,Debug构建总是在dtor中中断。
值得注意的是,这通常至少有一个调用站点远离DLL接口,那么在什么情况下,RVO将对象从DLL中移开而不复制它的二进制边界呢?在这件事上有点困惑。
发布于 2022-10-29 00:06:31
解决这个问题的最简单的方法通常是DLL导出MyData类(从内存中将dtor定义为虚拟也可以解决这个问题)。否则,由于dtor是内联的,因此您将得到dtor的两个副本,一个在原始DLL中,一个在exe中。DLL::getMyData()正在使用DLL中的堆创建一个新实例。然后,Exe试图使用自己的堆释放数据成员。MSVC然后发出一个错误,因为那个堆没有看到原始的分配。
对于std::string (或stdlib中的任何其他容器类型),我强烈建议至少使这些成员私有。如果程序员直接访问成员函数(例如保留),您很容易遇到同样的问题。
通常,MSVC会对此问题给出一个C4251警告。听起来这个警告可能已经在您的构建中被禁用了吗?
https://stackoverflow.com/questions/74241837
复制相似问题