4

虽然 Microsoft 标准运行时提供了分配函数的调试版本,但它并没有真正起作用,因为您不应该在 C++ 代码中使用裸 new,因此检测指向标准库,或者因为无论如何都无法检测标准库而无处可去。

现在我有了可以产生(和记录)分配回溯的代码,我也使用了 DUMA。然而,当我们使用流时,替换分配函数的尝试失败了,因为streambuf调用了一些调试变体,并且在 new 和 delete 之间这样做不一致。

那么有没有人在微软标准运行时通过覆盖函数而不是丑陋的预处理器技巧来替换分配器的经验?我怀疑它涉及避免调试分配器,但出于明显的原因我想保留_DEBUG定义(更多的调试代码取决于它)。

注意:我们目前使用 Visual C++ 9.0 (Visual Studio 2008)。

编辑:避免调试分配器不太可能是一个选项,因为 C++ 标准库需要在编译到库的函数和实例化以及用户代码中生成的实例化之间具有一致的 new 和 delete 定义,因为分配可能由一个人完成并由对方释放。顺便说一句,这意味着在包含强制的标题中定义静态内联变体不太可能削减它。

Edit2:动态链接是不可能的,因为 Windows 绑定来自特定 DLL 的符号,因此无法在链接时覆盖它们。但是我们不需要动态链接,也不要使用它,因为主要目标是 WinCE,静态链接是默认的。

4

1 回答 1

1

以下是我们的做法(使用 jemalloc,但任何其他分配器都是可能的):

  1. 将自定义内存分配器单独编译为 C 静态库。
  2. 将您的 C++ 应用程序与自定义分配器库链接。
  3. 覆盖运算符newdelete在您的 C++ 应用程序中调用自定义分配器。

笔记:

  • 自定义分配器必须用 C 而不是 C++ 编写。
  • 除非分配器驻留在单独的库中,否则无法确保足够早地初始化分配器。
  • 覆盖mallocandfree也是可能的,但在 MSVC 中要困难得多,因为它们不是“弱”链接的,并且因为 MSVC 中有很多自定义变体(例如使用/FORCE:MULTIPLE链接器标志)。

示例代码:

void* operator new(size_t size)
{
  void* ptr = my_malloc(size);
  if (ptr)
    return ptr;
  else
    throw std::bad_alloc();
}

void* operator new[](size_t size)
{
  void* ptr = my_malloc(size);
  if (ptr)
    return ptr;
  else
    throw std::bad_alloc();
}

void* operator new(size_t size, const std::nothrow_t&) throw()
{
  return my_malloc(size);
}

void* operator new[](size_t size, const std::nothrow_t&) throw()
{
  return my_malloc(size);
}

void operator delete(void* pointer) throw()
{
  my_free(pointer);
}

void operator delete[](void* pointer) throw()
{
  my_free(pointer);
}

void operator delete(void* pointer, const std::nothrow_t&) throw()
{
  my_free(pointer);
}

void operator delete[](void* pointer, const std::nothrow_t&) throw()
{
  my_free(pointer);
}
于 2016-12-09T10:29:02.540 回答