所以为了更好地理解new/delete(真的是为了用小例子向自己证明为什么接口需要虚拟析构函数),我想了解内存泄漏,这样我就可以生活在对它们的恐惧之中。但可以这么说,我很难把漏水漏掉。事实上,我也很难使用 new/delete。
这是我最简单的版本:
int* P1 = new int(43);
cout << "P1 = " << P1 << endl;
cout << "*P1 = " << *P1 << endl;
delete P1;
cout << "P1 = " << P1 << endl;
cout << "*P1 = " << *P1 << endl;
打印:
P1 = 0xcc0340
*P1 = 43
P1 = 0xcc0340
*P1 = 43
我在课堂上遇到了更复杂的事情,但这个例子说明了我的失败。我认为删除需要一个指针并释放它的内存,从而使指针或至少它指向的内容无效?我一定是在做一些非常简单非常错误的事情。
你正在造成未定义的行为。这意味着任何事情都可能发生。由于确实发生了某些事情,所以一切都按照记录进行。 (有时“某事”看起来与您可能错误期望的其他事情非常相似。完全按照您认为自己想要实现的目标进行操作是“未定义行为”可能允许的实例之一。)
另请注意,“内存泄漏”与您尝试执行的操作相反 - 在内存泄漏中您忘记释放内存,而您已经释放了内存并且现在正在访问无效内存。
这是真正的内存泄漏,它也不会导致未定义的行为——不要将“坏但正确”与“不正确”编程混淆!
int * factorial(int * n)
{
if (*n == 0) return new int(1);
else return new int(*n * *factorial(*n - 1));
}
我一定是做了一些非常简单非常错误的事情。
你说得完全正确。你的代码做了一些非常简单非常错误的事情,并且你付出了最终的代价 - 没有看到任何负面结果。有时候,你做错了事,但并没有什么不好的事情发生。这是最糟糕的结果,因为您可能没有意识到您的代码是错误的,并且您将继续使用它,直到有一天您的代码意外崩溃,并且您不知道在哪里或如何,因为它以前总是有效的。它会在与实际错误部分完全无关的地方损坏,您将花费数小时试图追踪它并找出它损坏的原因。
当你做错事时,这是未定义的行为。这意味着任何事情都可能发生。最好的情况是,你的代码崩溃,出现段错误,并且闪烁的灯亮起,表示“你正在使用释放的内存”,一切都非常清楚。最坏的情况下,恶魔会从你的鼻子里飞出来。但除此之外,第二个最坏的可能结果是一切似乎都按您的预期进行。
这将是内存泄漏:
int* P1 = new int(43);
P1 = new int(42);
分配内存而不再次删除。
这段代码
delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
导致未定义的行为。所以任何事情都可能发生。更容易导致内存泄漏:
for(;;) new int;
// Reserve some memory for an int and set that memory to the value 43.
int* P1 = new int(43);
// Print the address of the reserved memory.
cout<<"P1 = "<<P1<<endl;
// Print the contents of that memory.
cout<<"*P1 = "<<*P1<<endl;
// Free the memory - it is no longer reserved to you.
delete P1;
// int* P2 = new int(47);
// Print the address of the memory. It still holds the address to
// the memory that used to be reserved for you.
cout<<"P1 = "<<P1<<endl;
// Print the current value of the memory that used to be reserved.
cout<<"*P1 = "<<*P1<<endl;
如果取消注释 P2 行,很可能会为其分配相同的内存,这会更改最后一行打印的值。
访问已通过
delete
释放的内存会导致 未定义的行为,正如其他人指出的那样。未定义包括在某些情况下以奇怪的方式崩溃(也许仅在满月期间?;-)。它还包括目前运行良好的所有内容,但该错误是一个地雷,每当您在程序中的其他任何地方进行其他更改时,该错误都可能会触发。
内存韭菜是指您使用
new
分配内存并且从不使用 delete
释放内存。除非有人长时间运行您的程序并发现它耗尽了系统的所有内存,否则通常不会注意到这一点。
取消引用已删除的指针是未定义的行为,正如已经解释的那样,即您做错了什么。编译器可能会通过将指针的值更改为某个“magic”值来“帮助”您发现该错误,该值总是会导致取消引用指针失败,并且可以给您一个线索,表明这是因为它之前已被删除。 快速测试显示了我的以下行为: MSVC2010: 调试版本:*P1 = 0xfeeefeee 发布版本:*P1 =
海湾合作委员会 4.6:
调试和发布版本:*P1 = 0
所以你只是(不)幸运地再次获得了原始值。我建议您始终在调试模式下构建来测试您的程序(即使您没有调试),因为它会为您的代码添加额外的健全性检查。
但这不是内存泄漏。当您将指针指向另一个内存分配(或只是丢弃指针)而不释放它指向的内存时。然后第一个保持分配状态,并且由于您没有对它的引用,因此您无法释放它。