【发布时间】:2018-04-08 02:16:16
【问题描述】:
我有一些使用谷歌测试的 C++ 单元测试。将一些代码放在一起来覆盖 new/delete 运算符以检查单元测试中的泄漏。虽然有问题。一些 google 测试 new/deletes 使用我重写的方法,但有些没有,所以我在跟踪代码中得到错误错误——有时看到内存泄漏,即使它真的被删除了,有时看到 malloc 返回
这是我最小的新/删除覆盖(仅打印地址以供手动检查):
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);
}
这里是谷歌测试行,它没有通过我的覆盖删除(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 构建为存档文件,并在构建单元测试时将其链接。以防万一:我正在使用 cygwin 使用 mingw 构建 Windows。
这是一个最小的例子,两个文件 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并链接在一起:
/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
使用 1.8.0 版的 gtest,在 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_; 行不使用我重写的删除功能?
【问题讨论】:
-
因为
ptr_是nullptr? -
好主意,但不,否则我不会遇到不匹配问题。我只是再次在 gdb 中运行它并打印
ptr_以确保它不为空 -
考虑使用minimal reproducible example,而不是让我们猜测您在其他地方调用了哪种 UB。
-
如果问题与链接到外部库有关,解释可能是该库已编译/链接并使用其自己的(例如您的实现的默认值)运算符
new和delete.虽然该标准要求程序中的所有编译单元都使用这些函数的相同版本(即您所做的在理论上不是问题),但在使用链接库时,某些实现在这方面不太聪明。您可能想检查 mingw 是否已知存在此类问题(我不知道)。
标签: c++ new-operator googletest delete-operator