在具有多个返回点的函数中清除的正确方法

时间:2017-05-04 19:56:15

标签: c++ memory-management heap-memory

我有一个递归搜索算法,我想在每次调用后清理指针。但是,我在很多地方返回,在每个地点之前添加deletefree似乎很草率。

有更好的方法吗?我是否在函数返回时释放它们意味着我应该将它们分配到堆栈而不是堆中?

请注意,这是一个并行搜索(未在代码中显示),但调用者永远不会在其子代之前返回。这对使用堆栈有任何额外的缺陷吗?

示例代码(此处不要担心算法):

//create a new struct state (using new), initialize and return (C style)
new_state()

free_list(state* node)//free a list

double minimax(state* node, state* bestState) {

    if (base_case) {
        return;
    }

    state* gb = new_state(); //single node
    state* children = new_state(); //head of list

    generate_children(children); //fill list

    state* current = children; //traverse node

    //recurse on child
    double result = -minimax(current, gb);

    if (case1) {
        free(gb);
        free_list(children);
        return;
    }
    if (case2)  {
        //do stuff
    }

    while(current != NULL){
        result = -minimax(current, gb);
        if (case1) {
            free(gb);
            free_list(children);
            return;
        }
        if (case2)  {
            //do stuff
        }
        current = current->next;
    }
    free(gb);
    gb = NULL;

    //More stuff (with children but not gb)
    free_list(children);
    return;
}

3 个答案:

答案 0 :(得分:1)

  

然而,我在很多地方回来,在每一个地方放一个删除或免费似乎很草率。

是的。

  

有更好的方法吗?

是。智能指针是更好的方法。但是如果你不想丢弃你正在做的事情,并且在你可以继续之前学习如何使用智能指针(第一次可能很难)继续阅读下去。

  

我是否在函数返回时释放它们意味着我应该只将它们分配到堆栈而不是堆中?

是的,你可以这样做。它也会表现得更好。但如果你打算分配大量内存,它将无法工作。

  

请注意,这是一个并行搜索(未在代码中显示),但调用者永远不会在其子代之前返回。这对使用堆栈有任何额外的缺陷吗?

陷阱是一样的。使用并行代码,您必须小心。

有很多方法可以避免这个问题。已经提到了智能指针和堆栈分配。

另一种方法是只有一个退出点。这有时会变得笨拙,因为,例如,这意味着你必须在断开之前在你的循环中设置一个标志,以便知道它是成功终止还是由于错误。

另一种方法是在函数A中分配指针,调用函数B来完成实际工作,(传递分配的指针),然后一旦函数B返回函数A,释放指针。

答案 1 :(得分:1)

以下是RAII的一小部分样本:

首先,我们有struct只存储您的商品。

struct FreeAll
{
    state* gb;
    state* children;
    FreeAll(state* g, state* c) : gb(g), children(c) {}
    ~FreeAll() { free(gb); free(children); }
};

请注意,在销毁时,会在两个项目上调用free()。怎么用?

double minimax(state* node, state* bestState) 
{
    if (base_case) {
        return;
    }

    state* gb = new_state(); //single node
    state* children = new_state(); //head of list

    // Initialize our small RAII object with the above 
    // pointers   
    FreeAll fa(gb, children);

    generate_children(children); //fill list
    state* current = children; //traverse node
    //recurse on child
    double result = -minimax(current, gb);

    if (case1) {
        return;
    }
    if (case2)  {
        //do stuff
    }

    while(current != NULL){
        result = -minimax(current, gb);
        if (case1) {
            return;
        }
        if (case2)  {
            //do stuff
        }
        current = current->next;
    }
    //More stuff (with children but not gb
    return;
}

本地变量faFreeAll类型。当本地超出范围时,调用fa的析构函数,在free中存储的指针上调用struct。还要注意在返回点缺少任何代码来释放内存。这将在fa超出范围时完成。

请注意,这是一个简单的例子,并没有像其他方法那样复杂,但它为您提供了RAII范例的基本要点。

答案 2 :(得分:0)

ScopeGuard为您完成这项工作。

https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Scope_Guard

void your_function()
{
     Scope_guard const final_action = []{
            free(gb);
            free_list(children);};
     // your code here
 };