2012-10-12 237 views
3

我在调用DLL执行优化任务的模拟程序时遇到问题。在研究了这个问题一段时间之后,我认为我的问题在于DLL在返回了所需信息后用于释放内存的析构函数。所述仿真程序上的Borland C++生成器V6开发和DLL被在MS的Visual C++ 2005带指针的C++ Struct:构造函数和析构函数

对于该模拟程序(P),并交换数据的DLL开发,我创建两个结构InputCPLEXOutputCPLEX和功能optimize它有两个参数:一个类型为InputCPLEX的对象和一个类型为OutputCPLEX的对象。两个结构都在头文件structures.h中声明,该文件属于P项目和DLL项目。

两个InputCPLEXOutputCPLEX结构具有intint*成员,因此基本文件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提供更多信息(用于优化目的),因此在运行大型仿真(涉及大型数据集)时,仿真程序系统崩溃。额外的信息是两个结构中的指针,我猜测程序是在泄漏内存,所以我试图编写构造函数和析构函数,以便分配给结构inputoutput的内存可以正确管理。我想下面的代码,我发现搜索了互联网:

//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文件中删除了构造函数和析构函数,那么仿真程序仍然能够执行包含小数据集的小模拟。

非常感谢您的协助, David。

+0

对空指针的'delete'没有影响。析构函数中的“if”是不必要的。你应该遵循[三条规则](http://stackoverflow.com/search?q= [c%2B%2B-faq] + rule + of + three)。 –

+0

你好,你是什么意思 >空指针上的'delete'没有效果 你是说当我删除对象'输入'和'输出'它会自动删除它们的成员,所以我不需要在析构函数中声明? – David

+0

这意味着您在删除指针之前不必检查指针是否为NULL。 – juanchopanza

回答

1

你必须使用新的一致的方式 - 删除。如果new[]获取了某些内容,则应删除delete[],如果是new→删除delete。在您的代码中,您通过new创建了inputoutput,但是通过delete[]删除。

顺便说一句,你不必在删除之前检查零指针。 delete可以处理没有问题的零指针。

+0

感谢您的及时回复,我发布的代码中存在一个错误:我确实使用了delete而不是delete []来删除对象'input'和'output'。我纠正了我原来的帖子。 – David

1

我看到你的代码的几个问题:

1)内存泄漏/双缺失:

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

在这里,您覆盖sol指针由new int[n]分配的初始化和数据被泄露之后。你也可以在sol中双击删除指针 - 第二次在output的析构函数中。与var相同的问题 - 您将其删除两次,显式为delete[],并在input的析构函数中删除。

在使用delete添加析构函数之后引发了双删除问题,看起来像之前它不是问题。

也作为@里加提到你使用new[]来分配数组,但delete而不是delete[]在析构函数。这是不正确的,这是未定义的行为。尽管这看起来不像碰撞事故。在现实世界中,大多数编译器对于内置和POD类型实现deletedelete[]没有影响。严重问题只有在使用非平凡析构函数的对象数组时才会出现。

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

你总是应该在同一侧分配/释放内存。或者使用一些通用的低级API - 例如VirtualAlloc()/VirtualFree()HeapAlloc()/HeapFree()具有相同的堆处理。

+0

关于你的第一点:我将删除第一个数据分配'sol = new int [n];'。但是,我不确定我是否理解了双重删除:'var'和'sol'指针不是'InputCPLEX'或'OutputCPLEX'结构的成员。因此,它们不会被删除两次,而只会被明确的'delete []'删除一次。我不明白为什么'input'的析构函数会删除'var'。至于@Riga和你的建议,我已经更正了'input'和'output'的删除,但是我应该删除析构函数中的'delete'指令吗? (从你告诉我我认为我应该)。 – David

+0

关于你的第二点,你的猜测是正确的:'output-> outputData'被分配在DLL中。但是我可以在使用优化函数之前通过用虚拟值填充'outputData'来分配它,我应该这样做吗? – David

+0

@大卫是的,他们不是结构的成员,但他们拥有**分配的相同指针值**。指针的分配复制指针的值(它指向的地址),而不是它指向的结构。你可能有很多指向内存中相同结构的指针,但是只有一个**可以用来删除它。这被命名为**所有权**。 – Rost

0

这看起来很奇怪:

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

据我了解它返回的大小米,但与正 分配则通过指针(SOL)设置为outputData 覆盖阵列我想你意思是这样的:

int m = output->j; 
sol = new int[m]; 
memcpy(sol,output->outputData,sizeof(int)*m); 
+0

不好意思,'m'整数只是为了这个例子。我的意思是'sol = new int [n];',即我想分配一个大小为'n'的数组。 – David