3

我在调用 DLL 以执行优化任务的模拟程序时遇到问题。在研究了这个问题一段时间后,我认为我的问题在于我在 DLL 返回所需信息后用于释放内存的析构函数。仿真程序是在 Borland C++ Builder v6 上开发的,DLL 是在 MS Visual C++ 2005 上开发的。

为了模拟程序 (P) 和 DLL 交换数据,我创建了两个结构体InputCPLEXOutputCPLEX一个函数optimize,它接受两个参数:一个类型的对象InputCPLEX和一个类型的对象OutputCPLEX。这两种结构都在structures.h属于 P 项目和 DLL 项目的头文件中声明。

InputCPLEXOutputCPLEX结构都有和int成员int*,所以基本上文件structures.h看起来像:

//structures.h
struct InputCPLEX{
  public:
  int i;
  int* inputData;
}
struct OutputCPLEX{
  public:
  int j;
  int* outputData;
}

这个想法是,在模拟过程(P的执行)中,我定期调用DLL来解决优化问题,inputData与我的优化问题中的变量对应outputData的数组也是我的变量的最优值数组。我知道使用 STL 容器会更容易vector<int>,但是 - 如果我错了,请纠正我 - 在两个不同的编译器之间交换 STL 对象似乎很困难。

这是我的主文件(在 P 中)中的情况:

//main.h
InputCPLEX* input;
OutputCPLEX* output;
int* var;
int* sol;

//main.cpp    
[...] //lots of code
input = new InputCPLEX;
output = new OutputCPLEX;
int n = X; //where X is an integer 
var = new int[n]; 
[...] //some code to fill var
input->i = n; 
input->inputData = var; 
optimize(input,output); //calls the DLL
int m = output->j; 
sol = new int[n];
sol = output->outputData;
[...] //some code to use the optimized data
delete[] var;
delete[] sol;
delete input;
delete output;
[...] //lots of code

一年多以来,我一直在使用此代码,文件中没有任何构造函数或析构函数structures.h,因此没有执行结构成员的初始化。您可能已经猜到了,我不是 C++ 专家,事实上恰恰相反。我还想强调一下,我没有编写大部分模拟程序,只是一些功能,这个程序是由几个开发人员开发了 10 多年,结果相当混乱。

但是,直到最近,一切都运行良好。我决定向 DLL 提供更多信息(出于优化目的),因此在运行大型模拟(涉及大型数据集)时,模拟程序会系统性地崩溃。额外的信息是两个结构中的指针,我的猜测是程序正在泄漏内存,所以我尝试编写构造函数和析构函数,以便分配给结构的内存input可以output正确管理。我尝试了以下代码,我发现在互联网上搜索:

//structures.h
struct InputCPLEX{
  public:
  int i;
  int* inputData;
  int* inputData2; // extra info
  int* inputData3; // extra info
  InputCPLEX(): i(0), inputData(0), inputData2(0), inputData3(0) {}
  ~InputCPLEX(){ 
    if (inputData) delete inputData;
    if (inputData2) delete inputData2;
    if (inputData3) delete inputData3;
  }
}
struct OutputCPLEX{
  public:
  int j;
  int* outputData;
  int* outputData2;
  int* outputData3;
  OutputCPLEX(): j(0), outputData(0), outputData2(0), outputData3(0) {}
  ~OutputCPLEX(){ 
    if (outputData) delete outputData;
    if (outputData2) delete outputData2;
    if (outputData3) delete outputData3;
  }
}

但它似乎不起作用:程序崩溃得更快,只用了很短的时间。有人可以帮我找出代码中的问题吗?我知道可能还有其他因素影响我的程序的执行,但是如果我在structures.h文件中删除了构造函数和析构函数,那么模拟程序仍然能够执行小型模拟,涉及小型数据集。

非常感谢您的帮助,大卫。

4

3 回答 3

1

您必须使用一致的新方式 - 删除。如果某些东西被你获取了,new[]你应该删除它delete[],如果是new-> 删除delete。在您的代码中,您创建inputoutput通过new但删除 via delete[]

顺便说一句,您不必在删除之前检查指针是否为零。delete处理零指针没有问题。

于 2012-10-12T09:36:34.763 回答
1

我在您的代码中看到了几个问题:

1)内存泄漏/双重删除:

sol = new int[n];
sol = output->outputData; 

在这里,您sol在初始化后立即覆盖指针,并且分配的数据new int[n]被泄露。您还可以soloutput. 同样的问题var- 你删除它两次,通过显式delete[]和析构函数input

添加析构函数后会引发双重删除问题delete,看起来之前没有问题。

同样正如@Riga 提到的,您使用new[]分配数组,而delete不是delete[]在析构函数中。这是不正确的,这是未定义的行为。尽管这看起来不像是崩溃的原因。在现实世界中,大多数编译器对于内置和 POD 类型的实现delete并没有什么不同。只有当您使用非平凡析构函数的对象数组delete[]时才会出现严重问题。delete

2)在哪里output->outputData分配?如果在 DLL 中,这是另一个问题,因为如果在使用另一个编译器实现的 DLL 中分配内存,您通常无法在主程序中安全地释放内存。原因是new/delete主程序和 DLL 的运行时使用不同的实现和不同的堆。

您始终应在同一侧分配/取消分配内存。或使用一些常见的低级 API - 例如VirtualAlloc()/VirtualFree()HeapAlloc()/HeapFree()使用相同的堆句柄。

于 2012-10-12T10:35:39.947 回答
0

这看起来很奇怪:

int m = output->j; 
sol = new int[n];
sol = output->outputData;

据我了解,您以 m 为单位返回大小,但使用 n 进行分配,然后通过将指针(sol)设置为 outputData 来覆盖数组,我认为您的意思是:

int m = output->j; 
sol = new int[m];
memcpy(sol,output->outputData,sizeof(int)*m);
于 2012-10-12T10:47:30.927 回答