所以我知道堆是存储内存的地方。与堆栈不同,如果用户不删除堆,堆将保持存在。
然而,我有"对象已返回到内存堆"部分的问题。返回是否意味着对象已被释放?如果不是,为什么拥有指向该对象的指针/引用是一场灾难?
首先,堆和堆栈不是c++概念。它们指的是在某些系统上管理的特定类型的内存。
第二,通常被描述为"堆"的东西,在c++中被称为"动态分配内存"。
当动态分配的内存被程序释放时(例如,在使用new
操作符获得的东西上使用delete
操作符,在C的malloc()
返回的指针上使用free()
操作符),就程序而言,内存不再存在,但指针的值不会改变。在c++标准语言中,使用指针或引用指向不再存在的东西会产生未定义的行为。
实际上,内存可能存在于您的系统中,甚至可能仍然由主机系统分配给您的程序。然而,一旦它被程序释放,就没有什么可以阻止内存被重用了。您的程序可能会使用new
操作符来分配更多的内存,因此先前释放的内存现在被您的程序用于其他事情—与原始使用完全无关。这在本质上是危险的——结果可以是任何东西(这是c++标准中未定义行为的含义)。
操作系统也可能已经恢复了逻辑或物理内存(毕竟,您的程序已经指示不再使用该内存),并将其分配给另一个程序。这对您的程序和已分配内存的其他程序都是危险的。这就是为什么大多数现代操作系统都防止这种情况发生,例如,如果操作系统检测到对不再拥有的内存的访问,则强制终止程序。
真正的危险是,您可能会访问一个已释放的对象一段时间,而一切似乎都按要求工作。只是后来崩溃了。在你的程序释放一些内存和被其他地方重用之间,通常会有一段很长的时间间隔。
程序中的这种缺陷往往会激怒该程序的用户(由于一些不明确的原因,当程序在工作过程中以不可预测和不可重复的方式崩溃时,用户往往不太感激)。代码中的这种缺陷往往很难追踪,因此也很难纠正。
我有"对象已返回到内存堆"部分的问题。返回是否意味着对象已被释放?如果不是,为什么拥有指向该对象的指针/引用是一场灾难?
它可能有助于分解术语和澄清一些事情:
首先,术语Object在c++中指的是驻留在内存中的东西,但不是指内存本身。
对象和存储对象的内存是值得区分的,因为分配内存和初始化对象的操作是完全不同的。
下面这行做了两件事:
new int(123);
它在堆上为程序分配一块内存区域,其大小(以字节计)等于
sizeof(int)
。分配内存的作用不是创建对象,甚至也不会改变内存的内容;它只是为你的程序使用的内存圈护。
任何可能存在于该内存中的"垃圾"或垃圾值都不会被更改。在分配内存之后,该内存块用包含
123
的int
对象初始化。对象只能在已经分配给程序的内存区域内初始化。
既然new
操作符执行两种不同的操作,那么delete
操作符也可以执行两种不同的操作:
delete
将销毁对象;这通常仅限于释放对象所拥有的资源。销毁对象通常不会费心去改变或重置任何即将被释放的内存,因为这通常是浪费CPU周期。在对象被销毁后,您可能会发现该对象的残余或部分残余在内存中停留了一段时间,但就您的程序而言,对象本身已经死亡/消失,内存内容是垃圾。delete
将取消分配内存。取消分配的过程并不是要改变内存的内容,而只是释放内存供其他东西使用。
请记住,指针永远不能指向对象,因为对象是"内存中的东西"而不是内存本身;c++中的原始指针相当简单/愚蠢——它们不跟踪指向内存中的对象的状态。如果对象被移动、复制或销毁,指针将对它一无所知。
指针是内存中某个位置的地址。可以考虑指针值(地址)和包含这些值/地址的指针变量。遗憾的是,术语指针历来有一点双重含义,既指指针值又指指针地址,这似乎令人困惑。
通常,指针变量所指向的内存位置应该包含分配的内存区域内的有效对象,但也可能不是。c++不保护你不受悬空指针的侵害,其中悬空指针是一个指针变量,它包含一个尚未分配给程序的内存区域的地址。
c++也不保护你指向未初始化内存的指针(例如,如果你使用malloc
分配了内存,但没有初始化它的内容)。
最后,c++不能防止内存泄漏;作为程序员,你有责任跟踪所有分配的内存。
最后3点是new
/delete
在现代c++中不经常使用的众多原因之一,也是&
引用,std::vector
, std::shared_ptr
和其他替代品首选的原因之一;