我有很多代码,所以我会尝试尽可能少地向你们展示这些代码。
我正在编写一个泄漏内存的程序,我清理内存的努力导致我的程序崩溃(仅在Visual Studio中,不使用MinGw)。我正在使用Visual Studio 2015来调试我的代码,并查看我正在使用多少内存。但是,在添加delete
关键字以尝试释放某些内存时,Visual Studio会触发一个breakpont。当关注断点试图找出错误时,VS会将我带到一个“无源可用”的页面。
使用MinGw gcc编译相同的代码工作查找并执行正常,但我需要Visual Studio的调试器,以便我可以看到我的内存使用情况,以便我可以确定泄漏是否已修复。
我正在动态创建大量对象并重新为它们分配新对象,我需要帮助找出如何删除旧内存,以便我只能在内存中保留新创建的对象。
以下是我关注的代码
StateNode *initState = nullptr; // Pointer to the initial state
StateNode *finishState = nullptr; // Pointer to the final state
bool finished = false; // Flag for checking if the puzzle has completed
size = getNumQueens();
// Make dynamic 2D array of the specified size
char** init = new char*[size];
for (int i = 0; i < size; i++)
init[i] = new char[size];
// Puzzle main loop
while (!finished)
{
// Randomize the queens placement on the board
randomizeGame(init, size);
// Make the initial state with the current game board
initState = new StateNode(init, size);
// Run the hillclimbing algo
finishState = HillClimbing<StateNode>::Run(initState, size);
// Check to see if the algo returned a valid end state
if (finishState->getHeuristic() == 0)
finished = true;
else
{
// Try to clean up memory to prevent memory leak
delete initState; // This is where Visual Studio throws breakpoint
delete finishState;
}
}
正如您所看到的,此while循环通过将它们分配给initState来不断创建新的StateNode
对象。此外,HillClimbing::Run()
方法返回动态创建的StateNode并将其分配给finishState。
没有此代码:
else
{
// Try to clean up memory to prevent memory leak
delete initState; // This is where Visual Studio throws breakpoint
delete finishState;
}
我的程序泄漏了大量内存,当程序崩溃时接近2GB。 使用这些线VS会抛出断点,但是MinGw gcc没有,并且程序运行得更快。
我的主要问题:如何正确管理initState
和finishState
的内存以修复内存泄漏。
即。我如何只保留一个StateNode
对象,同时删除所有其他实例。
修改 这是VS输出窗口中的内容
The thread 0x4244 has exited with code 1857355776 (0x6eb50000).
HEAP[N-Queens.exe]: Invalid address specified to RtlValidateHeap( 01230000, 0126B540 )
N-Queens.exe has triggered a breakpoint.
当进入dissasembly并按F11继续执行代码时,最终会发生这种情况:
编辑2
StateNode.h
class StateNode
{
private:
char** state;
int heuristic;
int size;
public:
StateNode(char** state, int size);
int getHeuristic();
void printState();
char** getState();
};
以下是StateNode.cpp的代码
#include <iostream>
#include "state-node.h"
#include "heuristic.h"
/* Constructor, accepts a state and a size (the number of queens) */
StateNode::StateNode(char ** state, int size)
{
this->state = state;
this->size = size;
this ->heuristic = NQueens::CalcHeuristic(state, size);
}
/* Returns the heuristic value of the node */
int StateNode::getHeuristic()
{
return this->heuristic;
}
/* Prints the state with a nice like board for better visualization */
void StateNode::printState()
{
for (int i = 0; i < this->size; i++)
std::cout << " ____";
std::cout << std::endl;
for (int i = 0; i < this->size; i++)
{
for (int j = 0; j < this->size; j++)
{
if (j < this->size - 1)
{
std::cout << "| " << state[i][j] << " ";
}
else
{
std::cout << "| " << state[i][j] << " |";
}
}
std::cout << std::endl;
for (int k = 0; k < this->size; k++)
std::cout << "|____";
std::cout << "|\n";
}
}
/* Returns a copy of the nodes state */
char ** StateNode::getState()
{
return state;
}
答案 0 :(得分:2)
您当前的代码会分配动态分配的内存,但不具有谁拥有什么指针的连贯感。然后,弄清楚何时,何地以及谁负责释放内存变得很麻烦。修复这样的代码可能需要更多容易出错的逻辑来试图理清这些混乱。
而不是使用C ++和“new-less”代码,以下内容或多或少等同于您当前的代码:
#include <vector>
typedef std::vector<std::vector<char>> Char2D;
class StateNode
{
private:
char2D state;
int size;
int heuristic;
public:
StateNode(const Char2D& theState, int theSize);
int getHeuristic();
void printState();
Char2D& getState() { return state; }
};
然后你的构造函数就像这样:
StateNode::StateNode(const Char2D& theState, int theSize) :
state(theState),
size(theSize),
heuristic(NQueens::CalcHeuristic(state, size)) {}
当然,您的NQueens::CalcHeuristic
必须采用Char2D
(通过引用)而不是char**
。
然后其余的实现可能如下所示:
bool finished = false;
size = getNumQueens();
// Make dynamic 2D array of the specified size
Char2D init(size, std::vector<char>(size));
// Puzzle main loop
while (!finished)
{
// Randomize the queens placement on the board
randomizeGame(init, size);
// Make the initial state with the current game board
StateNode initState(init, size);
// Run the hillclimbing algo
finishState = HillClimbing<StateNode>::Run(initState, size);
// Check to see if the algo returned a valid end state
if (finishState.getHeuristic() == 0)
finished = true;
}
initState
和finishState
是两个不同的对象。此外,不需要else
块。
我知道这与原始代码有些不同,但目标应该是使用value
类型,如果需要,还有智能指针(我在这里看不到需要)。使用类型作为前面提到的类型是一种不会出现问题的方法。
如果您仍然想要指针路线,我仍然会单独留下vector
,并进行以下更改:
#include <memory>
//...
std::unique_ptr<StateNode> finishState;
// Puzzle main loop
while (!finished)
{
// Randomize the queens placement on the board
randomizeGame(init, size);
// Make the initial state with the current game board
std::unique_ptr<StateNode> initState = std::make_unique<StateNode>(init, size);
// Run the hillclimbing algo
finishState.reset(HillClimbing<StateNode>::Run(initState, size));
// Check to see if the algo returned a valid end state
if (finishState->getHeuristic() == 0)
finished = true;
}
此代码中没有泄漏,因为我们正在使用std::unique_ptr
,当指针超出范围或调用reset
时,会自动为您释放内存。
答案 1 :(得分:0)
您可以通过no-source-available调试代码。获取vs将反汇编和f11显示为下一个函数。
Vs具有带泄漏检测的调试堆。这可能会有所帮助,但也会导致速度减慢,并且早先崩溃。使用不同的C运行时编译以获得/丢失该功能。
应用程序验证程序也具有良好的泄漏检测功能,显示泄漏分配的堆栈。 这是我使用的