析构函数崩溃

8

我正在使用Visual Studio开发一个Win32 C++应用程序。

在其中一个源文件中,我有一个全局对象,如下所示。

TestClass tObj;

int main() //Execution starts here
{
}

TestClass在其他DLL中定义,如下所示。

struct Source
{

};

class TestClass
{
  list<Source> sourceList;
    public:
         TestClass() {}
        ~TestClass() {}
};

当我的应用程序正在运行时,如果我尝试通过关闭控制台窗口来显式关闭应用程序,则在TestClass析构函数中崩溃。调用堆栈显示CrtIsValidHeapPointer失败。

请帮助我解决这个问题。


使用四个空格缩进代码,普通文本不需要缩进。 - Marcelo Cantos
1
你是否使用相同的C++运行时构建了exe和DLL? - mmmmmm
是的,我都使用Visual Studio构建。唯一不同的是配置类型。一个是exe,另一个是在项目设置中的DLL配置。 - bjskishore123
你该如何将你的dll链接到exe文件?传统的静态链接方式(使用.lib文件)。 - YeenFei
使用相同的运行时库在exe和dll中可以解决崩溃问题。 谢谢大家的帮助。 - bjskishore123
6个回答

9
你的问题在于.exe和.dll之间不同的编译器/链接器设置会导致.dll和.exe使用标准库的不同实现:
  • 必须使用相同的预处理器标志来构建.exe和.dll,否则每个二进制文件将编译具有略微不同的实现。
  • 必须将.exe和.dll都链接到动态运行时。静态链接到运行时的二进制文件会有自己的堆 - 你最终会在一个堆上分配并尝试在另一个堆上释放。

要解决此问题,请转到 项目 > 属性 > 配置属性 > C/C++ > 代码生成 并将运行时库选项更改为 多线程调试 DLL (/MDd)。你必须对.exe项目和.dll项目都执行此操作。

从Visual Studio 2010开始,某些此类错误将使用 #pragma detect_mismatch 在链接时检测到。

*适用于所有具有标准库实现影响的预处理器标志


我对这个全面的答案点赞。@bjskishore123:请阅读FAQ。鼓励你接受你认为最有帮助解决问题的答案。 - sbi

4

请确保使用相同的运行时环境构建EXE和DLL,最好使用动态运行时环境。


通过在exe和dll中使用相同的运行时库来解决崩溃问题。感谢大家的帮助。 - bjskishore123

1

它在析构函数中崩溃,析构函数抛出了异常,调用终止并使您的应用程序崩溃。未捕获的异常

析构函数被调用有两种情况。第一种是在“正常”条件下销毁对象,例如当它超出范围或显式删除时。第二种是在堆栈展开期间由异常处理机制销毁对象。您必须在保守的假设下编写析构函数,即异常处于活动状态,因为如果控件由于异常而离开析构函数,而另一个异常仍然处于活动状态,则C++将调用终止函数。


全局对象在 main() 被调用之前被构造。 因此,我认为它的销毁是在 main() 退出后发生的。 那个时候,STL 内部使用的 list<Source> sourceList 头部在释放时无效。 因此,CrtIsValidHeapPointer 失败了。 - bjskishore123
问题是:为什么sourceList头无效?如果您的exe和DLL使用兼容的运行时正确构建,则此代码应该可以正常工作。运行时将在堆被释放之前清理全局对象。 - Seb Rose
崩溃问题可以通过在exe和dll中使用相同的运行库来解决。 谢谢大家的帮助。 - bjskishore123

1

全局对象是由 C 运行时初始化并销毁的。它们在调用 main 函数之前初始化,在其返回之后销毁。

错误很可能是由于从您的 TestClass 析构函数(或间接从 Source 析构函数)中访问了某些内容导致的。析构函数代码正在访问无效的内存(或已经被释放的内存)。

全局变量的初始化和销毁顺序未定义,并且通常是应用程序终止时错误的来源。如果还有其他全局变量可能清除或修改 TestClass 引用的资源,则这可能是问题所在。


我没有向sourceList(list <Source>)添加任何节点。 在析构函数调用时,列表为空。 但是,STL List内部将仍具有隐藏的Head指针。在尝试释放它时,会导致崩溃。 - bjskishore123
你是针对哪个CRT(C运行时库)构建EXE和DLL的? - Seb Rose

0

这个 DLL 和 EXE 是否使用相同的对齐方式(pack pragma)构建的?


0
尝试将构造函数和析构函数设为非内联,可能会有所帮助。如果构造函数和析构函数不是内联的,它们都将代表动态链接库生成,因此list<>的构造和销毁将使用相同的运行时库执行。 通常情况下,应避免在动态链接库边界传递不透明的STL对象。最好将它们封装为自己类的私有成员,并提供非内联方法来操作这些成员。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接