1

今天早些时候我问了这个问题

在花了一些时间调查这个问题之后,我发现了发生了什么。我将此作为一个新问题发布,因为我认为将其作为一个单独的问题进行跟踪很有趣。我将用答案更新该问题(以及指向此问题的链接)。

从调试器启动单元测试

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05560194     /* Different memory location */

从命令行启动单元测试

// Construct object
Object* pObject = new Object(...);
// Pointer value of pObject == 0x05176960

// Lots of other code
// ...

// Destroy object
delete pObject;

// Construct object again
pObject = new Object(...);
// Pointer value of pObject == 0x05176960     /* Same memory location */

总之:

  • 从命令行启动单元测试时,后续调用new分配一个Object(在分配新deleteObject之前)总是在内存中返回相同的地址。
  • 调试器启动单元测试时,后续调用new分配一个Object(在分配新deleteObject之前分配前一个)总是返回内存中的唯一地址。

问题是,因为Object在通过命令行启动时分配总是在内存中获得相同的地址,所以我正在访问的已存储指针的映射仍然可以使用并且测试不会崩溃。但是我希望我的单元测试在缺陷修复不到位时崩溃,以确保它不会静默失败并且缺陷不会再次出现。

我的问题有两个部分:

  1. 为什么堆管理器会在从命令行启动单元测试时重用内存的同一部分,但在我从调试器启动单元测试时却不会?

  2. 是否有我可以在我的测试工具上使用的编译器设置,或者我可以调用的方法来防止堆管理器重新使用我已删除的一段内存,以允许我正确编写我的单元测试? 1


1显然,这样做的一种方法是不删除原始对象,但分配这个的部分代码在我的生产代码中,我这样做会导致内存泄漏。

4

4 回答 4

2

您可以用您自己的具有您想要的行为的版本替换new和。delete

于 2010-11-25T05:56:35.910 回答
2

您的单元测试存在缺陷,因为它依赖于未定义的行为。您应该重写您的单元测试,使其不依赖于未定义的行为,在这种情况下,无论内存管理器如何决定分配内存,它都将始终通过。

你在做什么是这样的:

Object* pObject = new Object(...);
...
delete pObject;
pObject = new Object(...);
// Use dangling pointer to first object, and if it crashes, the unit test fails
// This is WRONG since a crash isn't guaranteed

相反,您应该重组单元测试,使其像这样工作:

Object* pObject = new Object(...);
...
// Check to see if there are dangling references to pObject right before we
// delete it.  If there are, assert() and fail the unit test.
assert(NoDanglingReferences(pObject));
delete pObject;
// Continue on with more tests
于 2010-11-25T06:04:37.257 回答
1

首先 - 不在“普通”内存管理器中。释放内存后,您将其所有权传递给内存管理器,后者可以重用它。

您可以按照用户 Andreas Brinck的建议编写自定义管理器,但它会做什么?它不会从空中制造内存,而是从 CRT 堆或操作系统堆之类的地方请求它。

场景 A. 它不会将内存返回到底层堆 - 您将发生泄漏,并且内存块仍将映射到地址空间并且可以访问。

场景 B. 它将内存返回到底层堆 - 然后当你的经理再次尝试分配内存时,底层堆可以再次返回该块。此外,当您将内存返回给它时,您也不知道底层堆会做什么。它可能会使其未映射或未映射-因此访问该内存可能会崩溃或不会崩溃。

底线是你搞砸了。尝试测试未定义的行为不会很有成效。

于 2010-11-25T06:30:38.647 回答
0

这是未定义行为的一个例子。C++ 或堆管理器都没有定义如何分配内存。您不能依赖于内存被重用或不被重用。当您执行上述操作时,无法确定或更改返回的指针是否与第一个分配的指针不同。

于 2010-11-25T05:45:18.283 回答