VS内存泄漏检测手工代码

手工版泄露检测,用法,平时用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&lt;*&gt;">
    <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>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值