6

我今天遇到了由我的 dll 和我的实际项目中的不同 CRT 设置 (MTd MDd) 引起的堆损坏。我发现奇怪的是,只有当我将 dll 中的析构函数设置为虚拟时,应用程序才会崩溃。有一个简单的解释吗?我知道我无法释放不在我的堆上的内存,但是当我将析构函数定义为非虚拟时,区别到底在哪里。

一些代码只是为了让它更清楚一点

动态链接库

#pragma once
class CTestClass
{
public:
    _declspec(dllexport) CTestClass() {};
    _declspec(dllexport) virtual ~CTestClass() {};
};

还有我的项目

int main(int argc, char* argv[])
{
    CTestClass *foo = new CTestClass;
    delete foo; // Crashes if the destructor is virtual but works if it's not
}
4

3 回答 3

2

之间有区别

class CTestClass
{
public:
    _declspec(dllexport) CTestClass() {}
    _declspec(dllexport) virtual ~CTestClass() {}
};

__declspec(dllexport) class CTestClass
{
public:
     CTestClass() {}
     virtual ~CTestClass() {}
};

在前一种情况下,您指示编译器仅导出两个成员函数:CTestClass::CTestClass() 和 CTestClass::~CTestClass()。但在后一种情况下,您将指示编译器也导出虚函数表。一旦你有一个虚拟析构函数,这个表是必需的。所以这可能是坠机的原因。当您的程序尝试调用虚析构函数时,它会在关联的虚函数表中查找它,但它没有正确初始化,因此我们不知道它真正指向的位置。如果您的析构函数不是虚拟的,那么您不需要任何虚拟函数表,一切正常。

于 2013-11-08T19:29:30.690 回答
0

您并没有真正发布足够的代码来确定。但是您的示例不应该崩溃,因为它没有任何问题:

int main(int argc, char* argv[])
{
    // 1. Allocated an instance of this class in *this/exe* heap, not the DLL's heap
    // if the constructor allocates memory it will be allocated from the DLL's heap
    CTestClass *foo = new CTestClass;

    // 2. Call the destructor, if it calls delete on anything it will be freed from the DLL's heap since thats where the destructor is executing from. Finally we free the foo object from *this/exe* heap - no problems at all.
    delete foo;
}

我怀疑在您的真实代码中,您必须对在 dll 的上下文中执行 operator new 的对象使用 operator delete 。如果没有 virtual 关键字,您可能会错过执行跨上下文删除的析构函数调用。

于 2013-08-11T22:57:30.140 回答
0

仅当您具有某些继承层次结构树时,才需要虚拟析构函数。Virtual 关键字将确保指向实际对象(不是对象的类型)的指针通过在 Vtable 中找到其析构函数来销毁。由于在此示例中,按照您提供的代码,CTestClass 没有从任何其他类继承,它在某种程度上是一个基类,因此不需要虚拟析构函数。我假设可能有另一个在幕后实现规则导致了这种情况,但你不应该将 virtual 与基类一起使用。每当您创建派生对象时,您也会创建它的基础(出于多态原因),并且基础总是被破坏(只有当您将其析构函数设为虚拟时,派生才会被破坏,因此将其放置在运行时 vlookup(虚拟)表中) .

谢谢

于 2013-10-10T16:58:34.180 回答