为什么在c++中将指向对象的指针或引用返回到内存堆是一场灾难

why it is a disater to have a pointer or reference to an object has been returned back to the memory heap in c++

本文关键字:内存 一场 返回 引用 c++ 指针 对象 为什么      更新时间:2023-10-16

所以我知道堆是存储内存的地方。与堆栈不同,如果用户不删除堆,堆将保持存在。

然而,我有"对象已返回到内存堆"部分的问题。返回是否意味着对象已被释放?如果不是,为什么拥有指向该对象的指针/引用是一场灾难?

首先,堆和堆栈不是c++概念。它们指的是在某些系统上管理的特定类型的内存。

第二,通常被描述为"堆"的东西,在c++中被称为"动态分配内存"。

当动态分配的内存被程序释放时(例如,在使用new操作符获得的东西上使用delete操作符,在C的malloc()返回的指针上使用free()操作符),就程序而言,内存不再存在,但指针的值不会改变。在c++标准语言中,使用指针或引用指向不再存在的东西会产生未定义的行为。

实际上,内存可能存在于您的系统中,甚至可能仍然由主机系统分配给您的程序。然而,一旦它被程序释放,就没有什么可以阻止内存被重用了。您的程序可能会使用new操作符来分配更多的内存,因此先前释放的内存现在被您的程序用于其他事情—与原始使用完全无关。这在本质上是危险的——结果可以是任何东西(这是c++标准中未定义行为的含义)。

操作系统也可能已经恢复了逻辑或物理内存(毕竟,您的程序已经指示不再使用该内存),并将其分配给另一个程序。这对您的程序和已分配内存的其他程序都是危险的。这就是为什么大多数现代操作系统都防止这种情况发生,例如,如果操作系统检测到对不再拥有的内存的访问,则强制终止程序。

真正的危险是,您可能会访问一个已释放的对象一段时间,而一切似乎都按要求工作。只是后来崩溃了。在你的程序释放一些内存和被其他地方重用之间,通常会有一段很长的时间间隔。

程序中的这种缺陷往往会激怒该程序的用户(由于一些不明确的原因,当程序在工作过程中以不可预测和不可重复的方式崩溃时,用户往往不太感激)。代码中的这种缺陷往往很难追踪,因此也很难纠正。

我有"对象已返回到内存堆"部分的问题。返回是否意味着对象已被释放?如果不是,为什么拥有指向该对象的指针/引用是一场灾难?

它可能有助于分解术语和澄清一些事情:

首先,术语Object在c++中指的是驻留在内存中的东西,但不是指内存本身。

对象和存储对象的内存是值得区分的,因为分配内存和初始化对象的操作是完全不同的。

下面这行做了两件事:

new int(123);
  • 它在堆上为程序分配一块内存区域,其大小(以字节计)等于sizeof(int)。分配内存的作用不是创建对象,甚至也不会改变内存的内容;它只是为你的程序使用的内存圈护。
    任何可能存在于该内存中的"垃圾"或垃圾值都不会被更改。

  • 在分配内存之后,该内存块用包含123int对象初始化。对象只能在已经分配给程序的内存区域内初始化。

既然new操作符执行两种不同的操作,那么delete操作符也可以执行两种不同的操作:

  • delete将销毁对象;这通常仅限于释放对象所拥有的资源。销毁对象通常不会费心去改变或重置任何即将被释放的内存,因为这通常是浪费CPU周期。在对象被销毁后,您可能会发现该对象的残余或部分残余在内存中停留了一段时间,但就您的程序而言,对象本身已经死亡/消失,内存内容是垃圾。

  • delete将取消分配内存。取消分配的过程并不是要改变内存的内容,而只是释放内存供其他东西使用。


请记住,指针永远不能指向对象,因为对象是"内存中的东西"而不是内存本身;c++中的原始指针相当简单/愚蠢——它们不跟踪指向内存中的对象的状态。如果对象被移动、复制或销毁,指针将对它一无所知。

指针是内存中某个位置的地址

可以考虑指针值(地址)和包含这些值/地址的指针变量。遗憾的是,术语指针历来有一点双重含义,既指指针值又指指针地址,这似乎令人困惑。

通常,指针变量所指向的内存位置应该包含分配的内存区域内的有效对象,但也可能不是。

c++不保护你不受悬空指针的侵害,其中悬空指针是一个指针变量,它包含一个尚未分配给程序的内存区域的地址。

c++也不保护你指向未初始化内存的指针(例如,如果你使用malloc分配了内存,但没有初始化它的内容)。

最后,c++不能防止内存泄漏;作为程序员,你有责任跟踪所有分配的内存。

最后3点是new/delete在现代c++中不经常使用的众多原因之一,也是&引用,std::vector, std::shared_ptr和其他替代品首选的原因之一;