我有一段代码正在创建基于图块的关卡。
class Level {
//Variables
//===================================================
public:
Tile *** TileGrid; //A 2d array of pointers to tiles
int TilesWide, TilesTall;
//Methods
//===================================================
public:
Level::Level(char * fileName);
Level::~Level();
void Draw();
};
我为 TileGrid 分配内存,一切都很好。我也为班级设置了一个析构函数。
Level::~Level() {
for (int i = 0; i < TilesTall; i++) {
for (int j = 0; j < TilesWide; j++)
//delete the looped tile being pointed to
delete TileGrid[i][j];
//delete the row
delete [] TileGrid[i];
}
//delete the array of rows
delete [] TileGrid;
}
为了咯咯笑,我决定删除关卡实例。这样做之后,我发现我仍然可以调用它的 Draw 方法。
在调试器中,TilesWide 和 TilesTall 的值是一个巨大的负数,因此在我的 for 循环迭代网格中没有任何内容。
尝试访问已删除的变量不会导致某种崩溃吗?
它不会必然导致崩溃。它可能会,也可能不会。它会导致所谓的undefined behavior:
未定义的行为
本国际标准没有要求的行为
它可能会像毁灭宇宙一样崩溃。
它可能导致崩溃。这取决于该内存是否被另一个对象覆盖和/或内存系统将该页面重新分配给另一个进程(或从当前进程中释放该页面)。底线是你不能依赖它工作(正如你已经知道的那样)
你所做的被称为“未定义的行为”。
而未定义的行为并不意味着“会崩溃”,它意味着任何事情都有可能发生。该程序可以格式化您的硬盘驱动器,可以播放巴赫奏鸣曲,可以在屏幕上画达菲鸭的图片,可以给您一个负值,并且在任何情况下它都会exactly您所要求的。
不包括崩溃,崩溃也是如此。
现在让我们假设一个合理合理的 C++ 实现。方法
Level::Draw
不会随着您传递给它的 Level
实例而变化,因此您调用的函数不依赖于实例。该实例作为一个指针作为参数传递给此函数,该指针指向堆中的某个内存,该内存曾经是该变量的副本。从那以后,它已被回收用于其他目的,或者可能包含有关堆的簿记信息——未定义的内容。 (而且,如果运行时将页面返回给系统,访问它可能会出现段错误)
然后你继续将垃圾解释为一些值。一切都很好,因为随机垃圾在大多数系统上几乎总是看起来像 C++
int
eger(在本例中为负整数)。
现在,一旦你开始取消引用指针(因此,访问内存的“随机”部分),或写入
this
的状态,崩溃很可能发生(如果不是那么,那么稍后,或其他地方)。
删除后访问对象是未定义的行为。未定义的行为可能意味着任何事情,从“看似成功”到“崩溃”再到“格式化硬盘”。