C++ operator delete override 并不总是被使用

C++ operator delete override not always used

我有一些使用 google 测试的 C++ 单元测试。拼凑一些代码来覆盖 new/delete 运算符以检查单元测试中的泄漏。虽然有一个问题。一些 google 测试 new/delete 使用我重写的方法,但有些没有,所以我在跟踪代码中得到错误的错误——有时看到内存泄漏,即使它真的被删除了,有时看到 malloc returns

这是我的最小 new/delete 覆盖(仅打印地址以供手动检查):

void * operator new(size_t size)
{
  void * addr = malloc(size);
  std::cout << "    tracking create: " << addr << "(size " << size << ")" << std::endl;
  return addr;
}
void * operator new[](size_t size)
{
  void * addr = malloc(size);
  std::cout << "    tracking create: " << addr << "(size " << size << ")" << std::endl;
  return addr;
}

void operator delete(void * addr) noexcept
{
  std::cout << "    tracking delete: " << addr << std::endl;
  free(addr);
}

void operator delete[](void * addr) noexcept
{
  std::cout << "    tracking delete: " << addr << std::endl;
  free(addr);
}

这里是 google 测试行,它没有通过我的覆盖删除 (gtest-port.h):

void reset(T* p = NULL) {
    if (p != ptr_) {
      if (IsTrue(sizeof(T) > 0)) {  // Makes sure T is a complete type.
        delete ptr_;
      }
      ptr_ = p;
    }
  }

当我在 gdb 中的 delete ptr_ 行中断时,然后步进,它直接进入 ptr_ = p 行,所以没有其他东西覆盖删除。

我将 gtest 构建为归档文件,并在构建单元测试时 link 将其放入。以防万一:我正在 windows 使用 cygwin 使用 mingw 进行构建。

这是一个最小的例子,2 个文件 min.cpp 和 minmain.cpp。这是 min.cpp:

#include <iostream>
#include <string>

// Overload the new/delete operators to check for memory errors
void * operator new(size_t size)
{
  void * addr = malloc(size);
  std::cout << "    tracking create: " << addr << "(size " << size << ")" << std::endl;
  return addr;
}
void * operator new[](size_t size)
{
  void * addr = malloc(size);
  std::cout << "    tracking create: " << addr << "(size " << size << ")" << std::endl;
  return addr;
}

void operator delete(void * addr) noexcept
{
  std::cout << "    tracking delete: " << addr << std::endl;
  free(addr);
}

void operator delete[](void * addr) noexcept
{
  std::cout << "    tracking delete: " << addr << std::endl;
  free(addr);
}

minmain.cpp:

#include "gtest/gtest.h"

TEST(MinTest, MinimalTest)
{
  int test = 5;
  test++;
  test++;
  test++;
  ASSERT_EQ(test, 8); 
}

int main(int argc, char *argv[])
{
  char* t = new char();
  t[0] = 't'; std::cout << "t is " << t[0] << std::endl;
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

编译:

/bin/x86_64-w64-mingw32-g++.exe -std=c++11 -D_USE_MATH_DEFINES -g -Wall -I../third_party/googletest-1.8.0/googletest/include -c min.cpp -o min.o

创建 min.o,然后编译 main 和 link 一起:

/bin/x86_64-w64-mingw32-g++.exe -std=c++11 -D_USE_MATH_DEFINES -g -Wall -I../third_party/googletest-1.8.0/googletest/include -o minmain minmain.cpp min.o ../third_party/googletest-1.8.0/googletest/make/gtest_main.a

使用 gtest 1.8.0 版本,在 gtest-port.h:1145 处中断以到达 delete ptr_ 行,然后执行。

下面是 运行 上面示例的一些示例输出(输出的前几行):

tracking create: 0x30e4c0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa477e0(size 392)
tracking create: 0xa47b80(size 28)
tracking delete: 0xa47b80

事实上,我被跟踪创建在同一地址上,中间没有跟踪删除,这是一个问题,因为中间有删除允许再次分配相同的地址,但这些删除没有经过我的重写删除运算符。

为什么 gtest 中的 delete ptr_; 行不使用我覆盖的删除功能?

看起来这是 MinGW 中的错误: MinGW bug #634

A work-around 是 link 静态版本的 libstdc++ 而不是让它 link 动态库。不是最理想的解决方案,但对于我的单元测试来说已经足够好了,它允许我正确覆盖。

我通过 compile/link 命令修改为以下内容:

/bin/x86_64-w64-mingw32-g++.exe -std=c++11 -D_USE_MATH_DEFINES -g -Wall -I../third_party/googletest-1.8.0/googletest/include -o minmain minmain.cpp min.o ../third_party/googletest-1.8.0/googletest/make/gtest_main.a /cygdrive/c/cygwin64/lib/gcc/x86_64-w64-mingw32/6.4.0/libstdc++.a

非常感谢 Peter 让我找到了正确的道路。