如何正确地重新使用指针对象来自定义 class

How to correctly re-use pointer object to custom class

我有很多代码,所以我会尝试用尽可能少的代码来展示给你们看。

我正在编写一个正在泄漏内存的程序,我清理内存的努力导致我的程序崩溃(仅在 Visual Studio,不使用 MinGw 中)。我正在使用 Visual Studio 2015 来调试我的代码,看看我使用了多少内存。但是,当添加 delete 关键字以尝试释放一些内存时 Visual Studio 会触发断点。当按照断点尝试找出问题所在时,VS 将我带到一个显示 'No Source Available'.

的页面

用 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 循环不断地创建新的 StateNode 对象,方法是将它们分配给 initState。此外,HillClimbing::Run() 方法 returns 一个动态创建的 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 不会,而且程序运行得更快。

我的主要问题:如何正确管理 initStatefinishState 的内存以修复内存泄漏。

即我怎样才能只在内存中保留一个 StateNode 对象,同时删除所有其他实例。

编辑 这就是 VS 输出中的内容 window

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.

当进入反汇编并按 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;
}

您可以通过 no-source-available 调试您的代码。获取 vs 以显示反汇编和 f11 进入下一个函数。

Vs 具有带泄漏检测的调试堆。这会有所帮助,但也会导致速度减慢和更早的崩溃。使用不同的 C 运行时编译以 gain/lose 该功能。

应用程序验证器也有很好的泄漏检测,显示泄漏分配堆栈。 这是我用的

您当前的代码分配动态分配的内存,但对于谁拥有什么指针没有连贯的认识。然后弄清楚何时、何地以及由谁负责释放内存变得很麻烦。要修复此类代码,可能需要更多容易出错的逻辑来解决问题。

而不是使用 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;
} 

initStatefinishState是两个不同的对象。此外,不需要 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 时自动为您释放内存。