手工版泄露检测,用法,平时用d_new替换普通new的均会被检测,注意placement new不要替换
性能:每次d_new和任何delete都要查表,估计是很慢的。
强类型
调试器可随时查看
输出可以跳转到源码
(补充代码可以实现快照对比,只需要复制和STL集合运算即可)
(理论上可以一个类型一个map,这里先不实现了)
限制:
(没加锁!根据实际需要要在对map访问的时候加锁哦~)
用了static inline变量,需要C++17,(理论上可以用其他语法代替,比如间接访问静态函数里的静态变量)
使用natvis可以让调试器观察的更方便,但不是必备的。
#include <iostream>
#include <type_traits>
#include <map>
#include <windows.h>
struct BaseInfoHolder
{
virtual int Print(char* s, size_t sizeOfBuffer, bool line_end) { return 0; }
};
template<class T>
struct ClassInfoHolder : BaseInfoHolder
{
T* ptr = NULL;
ClassInfoHolder() = default;
ClassInfoHolder(T* _ptr) : ptr(_ptr){}
virtual int Print(char* s, size_t sizeOfBuffer, bool line_end)
{
if constexpr (std::is_same_v<T, std::string>)
return sprintf_s(s, sizeOfBuffer, "Value: %s %p 【%16s】%s", typeid(T).name(), ptr, ptr->c_str(), line_end ? "\n" : "");
else
return sprintf_s(s, sizeOfBuffer, "Value: %s %p%s", typeid(T).name(), ptr, line_end ? "\n" : "");
}
};
struct AllocInfo;
class AllocRecordMap : public std::map<void*, AllocInfo>
{
public:
int working = 0x12345678;
~AllocRecordMap()
{
working = 0;
}
};
struct AllocInfo
{
int line;
const char* source;
void* _[2] = {};
BaseInfoHolder* classinfo = nullptr;
static inline AllocRecordMap g_allocRecords;
template<typename T>
T* operator | (T* ptr)
{
auto result = AllocInfo::g_allocRecords.try_emplace((void*)ptr, *this);
if (result.second == false)
__debugbreak(); //发生重复
AllocInfo& ai = result.first->second;
// Map的AllocInfo成员上覆盖构造ClassInfoHolder<T>
auto cih = new (&ai._) ClassInfoHolder<T>(ptr);
// 引用它方便在调试器中查看
ai.classinfo = cih;
return ptr;
}
static void PrintLeakMemory()
{
char outputbuff[256];
for (auto& v : g_allocRecords)
{
char _1 = 0;
char _2 = 0;
int outputed = snprintf(outputbuff, sizeof(outputbuff), "%s(%d) : Leak", v.second.source, v.second.line);
v.second.classinfo->Print(outputbuff + outputed, sizeof(outputbuff) - outputed, true);
printf("%s", outputbuff);
#ifdef OutputDebugString
OutputDebugStringA(outputbuff);
#endif
}
}
};
void operator delete(void* const block, size_t const) noexcept
{
if (AllocInfo::g_allocRecords.working == 0x12345678)
AllocInfo::g_allocRecords.erase(block);
operator delete(block);
}
void operator delete[](void* const block) noexcept
{
if (AllocInfo::g_allocRecords.working == 0x12345678)
AllocInfo::g_allocRecords.erase((void*)block);
operator delete(block);
}
#ifdef _DEBUG
#define d_new AllocInfo{ __LINE__, __FILE__} | new
#else
#define d_new new
#endif
int main()
{
std::string* pStr = d_new std::string("oh my god!a leak string");
d_new float(1.234);
auto pValue = d_new unsigned int{0x123};
auto pArray = d_new double[2]{};
AllocInfo::PrintLeakMemory();
delete pValue;
delete[] pArray;
char buff[100];
new (&buff) std::string();
puts("PrintLeakMemory:");
AllocInfo::PrintLeakMemory();
}
natvis:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="AllocInfo">
<MostDerivedType>classinfo</MostDerivedType>
<DisplayString>{classinfo}</DisplayString>
<Expand>
<ExpandedItem>classinfo</ExpandedItem>
<Item Name="source">source</Item>
<Item Name="line">line</Item>
</Expand>
</Type>
<Type Name="ClassInfoHolder<*>">
<Expand>
<Item Name="ptr">ptr</Item>
</Expand>
</Type>
<Type Name="BaseInfoHolder">
<MostDerivedType>this</MostDerivedType>
<DisplayString>{*this}</DisplayString>
<!--Expand>
<Item Name="ptr">this->ptr</Item>
</Expand>-->
</Type>
</AutoVisualizer>