C ++堆分配和内存重用

时间:2011-07-15 03:35:54

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

我有这个小片段:

Action* newAction(Agent& a, ACTION_MODE& m)
{
  Action *new_action;
  do
  {
    new_action = new Action(a,m);
  }
  while (!state->addAction(new_action));
  return new_action;
}
如果添加* a,

state->addAction(Action *a);将返回true,如果* a未添加则返回false(检查* a是否已存在)。

现在,我知道很多人认为goto被认为是邪恶的,所以,在上面的代码片段中,在每个循环中重新分配new_action而不删除它,是不是错了?

以下代码段不会更“明智”吗?

retry :
    Action *new_action = new Action(a,m);
    if (state->addAction(new_action))
    {
      return new_action;
    }
    else
    {
      delete new_action;
      goto retry;
    }

如果这看起来很简单,我很抱歉,但这是我一段时间以来一直想知道的事情。什么是正确的,删除​​内存然后重新分配,还是我可以立即重新分配?

编辑:

会更好吗?

Action* newAction(Agent& a, ACTION_MODE& m)
{      
  // state will only allow an action to be added if it does not already exist
  Action *new_action = new Action(a,m);

  if (!new_action) return 0;

  while (!state->addAction(new_action))
  {
    delete new_action;
    new_action = new Action(a,m);
  }

  return new_action;
}

此函数的调用者需要一个已添加到状态的新操作,因此必须在此处进行删除。

5 个答案:

答案 0 :(得分:7)

您在该代码中可能存在内存泄漏,因为每次循环都会创建一个 new 操作,但是如果无法添加,则不会释放它。更好的方法是将动作创建移到循环之外,例如:

Action *newAction (Agent& a, ACTION_MODE& m) {
    Action *new_action = new Action(a,m);
    while (!state->addAction(new_action))
        ;
    return new_action;
}

这比连续删除和重新创建操作更有效,应该优先使用。

我还修复了返回类型,这样编译器就不会抱怨,如果你永远无法添加动作,你应该引入某种失败策略。

类似的东西:

Action *newAction (Agent& a, ACTION_MODE& m) {
    // Try to make action, return null if no go.

    Action *new_action = new Action(a,m);
    if (new_action == 0)
        return 0;

    // Try a limited number of times to add action to state.

    int limit = 10;
    while (!state->addAction(new_action)) {
        if (--limit == 0)
            break;
        // Optional sleep if desired.
    }

    // If that didn't work, delete action and return null.

    if (limit == 0) {
        delete new_action;
        return 0;
    }

    // Otherwise the action was added, return it.

    return new_action;
}

答案 1 :(得分:2)

使用shared_ptrunique_ptr会更明智。你不应该再在C ++中进行自己的内存分配了。

无论如何,我认为这不是一个使用goto的好地方。您正在实现循环,这正是循环结构的用途。

以下是使用unique_ptr

的方法
unique_ptr<Action> new_action;
do {
  new_action.reset(new Action(a,m));
} while (!state->addAction(std::move(new_action)));  // accept by &&

答案 2 :(得分:2)

  

以下代码段不会更“明智”吗?

没有。这将更加合理:

do
{
  new_action = new Action(a,m);
}
while (!state->addActionOrDelete(new_action));

addActionOrDelete完全按照其说法行事。如果要将指针的所有权转移到另一个函数,则该其他函数必须对该指针的所有权负责。如果无法添加,则必须删除它。

但说实话,我不明白你为什么要在循环中分配这些Action个对象。 state的状态是否会发生变化,以至于之前添加的操作无效,但以后可能会有效吗?如果是这样的话,在可以添加操作之前让state->addAction阻塞会更有意义吗?

为什么分配呢?这看起来像Java或C#风格的代码。就这样做:

while(!state->addAction(Action(a,m))) ;

这将创建临时Action个对象。无需堆分配。甚至更好:

Action theAction(a, m);
while(!state->addAction(theAction)) ;

这些物品难以复制吗?

答案 3 :(得分:2)

如果您想保留当前的代码上下文,请进行2项小改动:

  Action *new_action = 0;  //<-- (1) initialize to 0
  do
  {
    delete new_action;   // (2) delete the previous pointer
    new_action = new Action(a,m);
  }
  while (!state->addAction(new_action));

修改: 我从@ paxdiablo的回答中意识到你实际上不需要继续分配和释放内存。简单地这样做,正如他所说:

Action *new_action = new Action(a,m);
while(!state->addAction(new_action));
return new_action;

[注意:如果您要接受我的答案,请选择他的答案。]

答案 4 :(得分:1)

我会这样写:

for ( ; ; ) {
    Action* new_action = new Action(a, m);
    if (state->addAction(new_action)) return new_action;
    delete new_action;
}

如果您不喜欢从循环中间返回,请将其更改为break,但这意味着您必须将new_action的声明移到循环之外。

我总是尝试配对newdelete。在这个函数中有一个,在它调用的函数中有一个对我来说似乎是一个坏习惯。