【问题标题】:Memory Management Confusion C++内存管理混乱 C++
【发布时间】:2012-04-16 21:55:27
【问题描述】:

我无法理解 C++ 中的一些基本内存管理原则。此代码是循环的一部分,循环是将迷宫文件读入二维向量的函数的一部分。

根据 Valgrind 的说法,以下代码导致内存泄漏...

注意t 是一个MazeNode 对象,vertst 类中的一个向量,它包含指向节点对象的指针(不要与MazeNode 对象混淆):

node* top = new node(TOP, rowCount, i, t.type);
node* bot = new node(BOTTOM, rowCount, i, t.type);
node* left = new node(LEFT, rowCount, i,  t.type);
node* right = new node(RIGHT, rowCount, i, t.type);

t.verts.push_back(top);
t.verts.push_back(bot);
t.verts.push_back(left);
t.verts.push_back(right);

temp.push_back(t);

top = NULL;
bot = NULL;
left = NULL;
right = NULL;
delete top;
delete bot;
delete left;
delete right;

最初我没有在删除它们之前将每个指针设置为 NULL,但会出现分配错误。所以我只是将它们设置为 NULL 并且我的代码有效。我想我真的很困惑为什么这会导致内存泄漏以及为什么我需要将指针设置为 NULL。可能有一种更简单的非指针方法可以做到这一点,但也许这个问题会帮助我更好地理解内存管理。

谢谢大家。

编辑:这是 MazeNode 类(这就是“t”)(也请原谅我写这个类的懒惰,让所有东西都像结构一样公开)

class MazeNode 
{
public:
    void setType(char c);
    char getChar();

    NodeType type;
    vector<Direction> visitedFrom;

    vector<node*> verts;
};

还有节点类:

class node
{
public:
    node();
    node(Direction d, int r, int c, NodeType t);
    ~node(); //empty definition
    node(const node* n);
    node& operator=(const node& n);

    void addAdj(node* a, int w);
    void printAdj() const;
    string direction() const;

    void print() const;
    bool operator<(const node& n) const;


    int distance; //from start
    bool visited;
    node* prev;
    vector<Edge> adj;
    Direction dir;
    int row, col;
    NodeType type;
};

EDIT2:谢谢大家。我现在明白了这个问题。我更改了指针对象的向量,以便不再使用指针。

【问题讨论】:

  • 您不需要将指针设置为 NULL。这似乎正是导致泄漏的原因。
  • 请添加t.verts的定义。
  • 我是这么想的,但是如果我只使用 delete 为什么会出错呢?
  • 请发布您的ttemp 变量的定义。我很确定导致泄漏的不仅仅是设置为 NULL,但我需要变量定义来显示这里的“全部”错误。
  • Don't use new, don't use delete。 C++ 的这些特性并不适合大多数代码。

标签: c++ pointers memory-management dynamic


【解决方案1】:

在添加 null 赋值之前,您的代码有一个与内存泄漏不同(更严重)的问题:存储并且可能还使用了一个杂散指针,即指向已释放内存的指针。

通过添加空分配使其成为内存泄漏会使其更好,但不会太多。

真正的解决方案是在调用 delete 之后不要将任何指针保留在任何地方。 也就是说,这里不要push_back或不要delete

【讨论】:

  • “真正的解决方案是在调用 delete 之后不要将任何指针保留在任何地方。” ....好吧,取决于。对于简单的场景,这可能是一个很好的经验法则,但对于复杂的场景,您最终将拥有可重用的指针数组等。你可能想让你的措辞更宽松一点,在没有绝对的地方不是绝对的。
  • 对不起,我对此有点陌生,但是我怎样才能有一个指针向量,而不退缩呢?我应该不使用指针对象的向量吗?
  • @Kaganar - 这当然是一种简化,但在第一次查看 SIGABORT 时很实用。
  • @Slims - 你绝对可以有一个指针向量。但是,我怀疑您的示例中的 ttemp 未在其全部范围内显示。如果这些变量中的任何一个(或其中任何一个的副本)仍然存在并在delete 之后使用,那么您正在走在雷区。指针对取消引用变得危险。发布更多代码可能是为了展示你对它们做了什么,或者你曾经将它们传递给哪个库。
【解决方案2】:

您将指针放入容器中,然后删除指针。当您的代码稍后尝试使用这些指针时,它们是无效的并导致崩溃。

通过在删除它们之前将指针设置为 NULL,您最终根本不会删除它们 - 删除 NULL 指针不会做任何事情。但是现在没有什么可以删除对象了,你会得到内存泄漏。

你需要在代码中找到不再使用指针的地方,并在那里删除它们。

编辑:当然我应该提到像std::shared_ptr 这样的智能指针完全消除了这个麻烦,因为它会自动删除对象。

【讨论】:

    【解决方案3】:

    您在删除它们之前将值设置为 NULL,因此您尝试删除 NULL 并且没有被删除。尝试将删除调用移到 NULL 调用之上。

    【讨论】:

      【解决方案4】:

      这种困惑正是我为这些事情创建宏的原因:

      #define delobj(obj) (delete obj, obj = NULL)
      

      你会这样使用它:

      delobj(top);
      delobj(bot);
      delobj(left);
      delobj(right);
      

      【讨论】:

        【解决方案5】:

        错误是使用指针向量。根据你的说法,verts 是这样的:

        vector<node*> verts;
        

        但它应该是这样的:

        vector<node> verts;
        

        在第一种情况下,当你 push_back() 指针时,没关系,但是当你 pop_back 或以其他方式重新调整向量的大小时,指针是向量的“内容”,并且被取消分配,但不是指针指向什么,也就是节点。因此节点泄漏。但在第二种情况下,节点是向量的“一部分”,并且在重新调整向量大小的过程中被分配/解除分配。

        您在此处的模式可能表示 Java/C# 背景,因为在这些语言中“新建”到容器中非常常见,但要在 C++ 中做到这一点,您需要一个智能指针容器(如 vector&lt;shared_ptr&lt;node&gt;&gt;或其他东西),这可能超出了问题的范围。但是在那些语言中,对引用类型的每个引用都是一个“智能指针”(或多或少),因此这是自动完成的。 C++ 不是这样的。

        您要么需要更改代码以使用vector&lt;node&gt;(并更改您推回它的方式),要么需要在向量缩小时显式取消分配节点。

        【讨论】:

        • 我明白了。那么向量接下来会缩小。它用于整个程序;它只是一个固定迷宫的结构。它永远不应该缩小。
        【解决方案6】:

        改为:

        delete top;
        delete bot;
        delete left;
        delete right;
        
        top = NULL;
        bot = NULL;
        left = NULL;
        right = NULL;
        

        它应该可以工作。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2011-12-27
          • 1970-01-01
          • 1970-01-01
          • 2017-04-30
          • 2013-04-16
          • 2016-01-21
          • 2012-07-31
          相关资源
          最近更新 更多