为什么在删除类实例后仍然可以访问它们
Why can still access class instances after deleting them
所以我正在构建一个链表类,具有类似堆栈的功能(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。但是,即使在删除实例之后,我仍然可以访问实例并输出其成员。我不知道这是否重要,但实例在删除时有三个不同的指针指向它。它们是:
- 节点*滑块;
- 节点*结束;
- 前一个类实例的Node::Node *nextNode;
我想现在该问个问题了:D
如果我在删除实例成员后仍然可以访问它,我怎么知道它是否被正确删除?
我的代码会导致内存泄漏吗?
我想最终的问题是:我做错了什么吗?注:这里有几个变量(position2df; vector2df;
)来自irrlight Game Engine。我只是想指出来避免混淆。
如果我在这篇文章中含糊不清,请原谅我,但我在网上提问方面不是很熟练。
如果我在删除实例成员后仍然可以访问它,我怎么知道它是否被正确删除?
只要相信运行库:如果你调用delete
,这个东西就会被删除,句号。一旦你告诉系统你不再需要内存,你就放弃了它——它不再是你的了。系统将其与可重新分配的可用内存一起计算,因此它可以根据请求将其提供给程序的其他部分。你仍然可以访问那个内存,甚至可以找到在你放弃它之前那里有什么,但这是未定义的行为。
我的代码会导致内存泄漏吗?
不,因为你释放了你分配的内存。
我想最终的问题是:我做错了什么吗?
Yes -访问已分配对象的内存是错误的(未定义的行为)。除此之外,它看起来很好。
如果东西在被删除后被访问,这不是泄漏,但它是未定义的行为:也就是等待发生的崩溃。
删除的对象不会立即覆盖。它们所在的内存区域仅仅被标记为"免费使用"。在内存被重新分配和初始化用于其他目的之前,它不会被覆盖。
你无法控制何时以及为什么会发生这种情况,所以你不应该在删除内存后访问它。为了确保这一点,所有指向已删除对象的指针都应该在删除对象之前设置为空。
当使用new和delete时,您正在使用动态内存:您要么保留内存块,要么释放它。在释放时,它只是被注释为免费使用,因此可以在需要时重用它。但是,程序并不会清除该内存区域。
有一个指针指向一个释放的内存区域是可能的,你可以从那里读取数据,但要小心,因为在那里找到的数据可能会在任何时候被替换,当程序需要保留更多的内存。
访问已释放的内存区域在任何时候都是危险的,可能会导致程序崩溃,甚至更糟,导致故障。
如果我在删除实例成员后仍然可以访问它,我该怎么做知道它是否被正确删除了?
调用delete
后,内存不再是你的,对象被认为已删除
我的代码会导致内存泄漏吗?
不,如果你没有调用delete
,那么它将是一个泄漏,但是这里你释放了你之前分配的内存,所以它是OK的。
我想最终的问题是:我做错了什么吗?
是的,在调用delete
之后,你不应该假设仍然有你的对象,它是未定义行为
为什么删除类实例后仍然可以访问它们?
你不能。
如果仍然有一个指针指向包含该对象的内存,则可能无法访问它的残余部分;或者可能是在对象被删除后创建的另一个不相关的对象。如果您非常幸运,内存可能已经被取消映射,并且您将得到一个友好的分割错误来指出错误,但这通常只发生在非常大的对象上。
简而言之,对一个已删除对象的指针解引用会产生未定义的行为。
我怎么知道它是否被正确删除了?
通过知道程序何时删除了它,并确保之后不使用任何悬空指针。如果您使用原始指针并直接调用delete
,这是相当棘手的,这就是为什么您通常应该使用RAII类型(如容器和智能指针)管理动态内存。
我的代码会导致内存泄漏吗?
据我所知,pop
函数没有泄漏;如果你使用智能指针,你可以使它更明显地正确。
析构函数不删除任何东西,所以,除非你有其他东西负责删除列表的内容,否则当列表被销毁时,你将泄露列表中剩余的任何东西。
我想最终的问题是:我做错了什么吗?
如果确实添加了析构函数来修复泄漏,则还需要添加(或删除)复制构造函数和复制赋值操作符,以防止复制列表时的双重删除。这有时被称为"三法则",在编写资源管理类时非常重要。如果你使用智能指针,它们会为你处理好这一切,你可以遵循更简单的"零规则"(即让编译器为你生成这三样东西)。
- 为什么示例代码访问IUnknown中已删除的内存
- 删除目录函数访问被拒绝
- 编写一个函数来删除单链表中的节点(尾部除外),仅授予对该节点的访问权限
- 从嵌套循环中的 std::list 中删除将返回访问冲突
- 在C++中删除双向链表的头节点后出现访问冲突异常
- c++ 循环访问对象列表并删除对象
- C++:在进行切片时对迭代器的约定,特别是对于访问最后一个元素并最终将其删除
- 尝试从单向链表C++中删除单个节点时出现读取访问冲突
- C++访问冲突读取位置0xDDDDDDCD当我尝试删除已更新的数组时
- C++ 基类调用已删除或无法访问的函数
- 使用 free() 或删除时访问冲突
- 可以删除SQLite3数据库文件,并且仍然可以访问
- 为什么在 C++ 执行删除操作后仍可以访问释放的动态分配的内存
- 访问控制对于已删除的构造函数是否重要?
- 在循环访问嵌套向量时删除元素
- 从函数中删除动态内存分配,但无法访问变量
- 删除函数 c++ 的读取访问冲突异常
- 删除后访问指针
- 删除复制构造函数和运算符=类范围访问
- 为什么我可以访问已删除但在C++中具有虚拟析构函数的对象?