【问题标题】:Why can still access class instances after deleting them [duplicate]为什么删除后仍然可以访问类实例[重复]
【发布时间】:2013-04-12 17:43:06
【问题描述】:

所以我正在构建一个具有类似堆栈功能 (LIFO) 的链表类来保存 Node 类的实例:

enum Sides{NorthWest=0, North=1, NorthEast=2, West=3, East=4, SouthWest=5, South=6, SouthEast=7};

class Node
{
    public:
    Node(position2df pos, int id):nextNode(NULL)
    {
        position=pos;
        ID=id;
    }
    ~Node(){}

    position2df getPosition(){return position;}
    int getID(){return ID;}

    void setPeripheralID(Sides side, int id){peripheralID[side]=id;}
    int getPeripheralID(Sides side){return peripheralID[side];}

    Node *nextNode;

    private:
    position2df position;
    int ID;
    int peripheralID[8];
};

class NodeList
{
    public:
    NodeList()
    {
        root=NULL;
        end=NULL;
    }
    ~NodeList(){}

    /// Function for adding elements to the list.
    void push(position2df pos, int id)
    {
        if(root==NULL)
        {
            root=new Node(pos, id);
            end=root;
        }
        else
        {
            Node *newend=new Node(pos, id);
            end->nextNode=newend;
            end=end->nextNode;
        }
    }

    /// Function for removing objects from the list.
    Node *pop()
    {
        slider=root;
        Node *previous;
        Node *next=slider;

        for(previous=NULL; next!=NULL; next=slider->nextNode)
        {
            previous=slider;
            slider=next;
        }

        delete slider;
        end=previous;
        cout << "Can still access deleted object: " << (*slider).getID() << endl;

        return end;
    }

    private:
    Node *root;
    Node *end;
    Node *slider;
};

NodeList::Node *pop() 函数中(其目的是删除最后一个元素并将前一个元素重置为列表的末尾),我在(指针名称)滑块指向的Node class 实例上调用delete。但是,即使在删除它之后,我仍然可以访问该实例并输出它的成员。我不知道这是否重要,但实例在删除时有三个不同的指针指向它。它们是:

  • 节点 *slider;
  • 节点 *end;
  • 上一个类实例的 Node::Node *nextNode;

我想在这一点上一个问题是有序的:D

如果删除后我仍然可以访问实例成员,我如何知道它是否被正确删除?

我的代码会导致内存泄漏吗?

我想最终的问题是:我做错了吗?

P.S. 这里有几个变量 (position2df; vector2df;) 来自 Irrlicht 游戏引擎。只是想指出这一点以避免任何混淆。

如果我对这篇文章含糊不清或不清楚,请原谅我,但在网上提问时我不是很熟练。

【问题讨论】:

标签: c++


【解决方案1】:

如果删除后我仍然可以访问实例成员,我如何知道它是否被正确删除?

只要相信运行时库:如果你调用delete,这个东西就会被删除,句号。一旦你告诉系统你不再需要内存,你就放弃了——它不再是你的了。系统用它的可用内存来计算它以重新分配,因此它可以根据请求将它提供给程序的其他部分。您仍然可以访问该内存,甚至可以找到您放弃之前的内容,但这是未定义的行为。

我的代码会导致内存泄漏吗?

不,因为你释放了你分配的内存。

我想最终的问题是:我做错了吗?

是的 - 访问已释放对象的内存是错误的(未定义的行为)。除此之外,它看起来不错。

【讨论】:

  • 所以我必须手动 NULL(ify) 指向现在已删除的内存块的指针,对吗?
  • @Aluthren 清空已删除的指针是一种非常常见的做法,是的。你没有必须这样做,但它绝对不会受到伤害。
【解决方案2】:

如果东西在被删除后被访问,这不是泄漏,而是未定义的行为:也就是等待发生的崩溃。

【讨论】:

    【解决方案3】:

    已删除的对象不会立即被覆盖。他们所在的内存区域仅仅被标记为“免费使用”。在为其他目的重新分配和初始化内存之前,它不会被覆盖。

    您无法控制发生这种情况的时间和原因,因此您不应该在删除内存后访问它。为确保这一点,所有指向已删除对象的指针都应在删除之前设置为 null。

    【讨论】:

    • 但通常在删除后将所有指针设置为 NULL 是不可能的,因为该指针可能已被复制多次,而您不知道这些指针在哪里。 IMO 这是误导性建议,对典型情况没有帮助,并且可能表明可以使指针使用变得更安全。
    【解决方案4】:

    当使用 new 和 delete 时,您正在使用动态内存:您要么保留一个内存块,要么释放它。释放时,它只是被注释为免费使用,因此可以在需要时重复使用。但是,该程序并没有专门清理该内存区域。

    有一个指向已释放内存区域的指针是可能的,并且您可以从那里读取数据,但要小心,因为当程序需要保留更多内存时,可能会随时替换在那里找到的数据。

    访问已释放的内存区域在任何时候都是危险的,可能会导致程序崩溃,甚至更糟糕的是,出现故障。

    【讨论】:

      【解决方案5】:

      如果删除后我仍然可以访问实例成员,我该怎么办 知道它是否被正确删除?

      调用delete后,内存不再属于你,对象被视为已删除

      我的代码会导致内存泄漏吗?

      不,如果你还没有调用delete,那就是泄漏,但是这里你释放了你之前分配的内存,所以没关系。

      我想最终的问题是:我做错了什么吗?

      是的,在调用delete 之后,您不应该假设仍然存在您的对象,这是未定义的行为

      【讨论】:

        【解决方案6】:

        为什么删除类实例后还能访问?

        你不能。

        如果您仍然有一个指向包含该对象的内存的指针,那么您可能无法访问其残余部分;或者可能是在删除对象后创建的另一个不相关的对象。如果你很幸运,内存可能没有被映射,你会得到一个友好的分段错误来指出错误,但这通常只发生在非常大的对象上。

        简而言之,取消引用指向已删除对象的指针会产生未定义的行为。

        我怎么知道它是否被正确删除?

        通过了解您的程序何时将其删除,并确保您之后不使用任何悬空指针。如果您使用原始指针并直接调用delete,这将非常棘手,这就是为什么您通常应该使用容器和智能指针等 RAII 类型来管理动态内存。

        我的代码会导致内存泄漏吗?

        据我所知,pop 函数没有泄漏;如果你使用智能指针,你可以让它更明显正确。

        析构函数不会删除任何东西,所以除非你有其他东西负责删除列表的内容,否则当列表被销毁时,你会泄漏其中的任何内容。

        我想最终的问题是:我做错了什么吗?

        如果您确实添加了一个析构函数来修复泄漏,您还需要添加(或删除)复制构造函数和复制赋值运算符,以防止在复制列表时重复删除。这有时称为Rule of Three,在编写资源管理类时非常重要。如果您要使用智能指针,它们会为您处理所有这些,您可以遵循更简单的“零规则”(即让编译器为您生成这三件事)。

        【讨论】:

          猜你喜欢
          • 2016-03-07
          • 2012-10-23
          • 2014-05-14
          • 1970-01-01
          • 2018-01-05
          • 1970-01-01
          • 1970-01-01
          • 2010-10-08
          • 2021-06-09
          相关资源
          最近更新 更多