C++內存泄漏檢測(調試工具)


理論

  • 什么是內存泄露:指因為疏忽或錯誤造成程序未能釋放已經不再使用的內存的情況。內存泄漏並不是指內存在物理上的消失,而是應用程序分配某段內存后,因為設計錯誤,失去了對該段內存的控制,因而造成了內存的浪費。

工具作用

  • 在使用Debug版的malloc分配內存時,malloc會在內存塊的頭中記錄分配該內存的文件名及行號。
  • 當程序退出時會在main()函數返回之后做一些清理工作,這個時候來檢查調試堆內存,如果仍然有內存沒有被釋放,則一定是存在內存泄漏。
  • 從沒有被釋放的內存塊,就可以獲得文件名、行號,泄漏多少字節,會打印顯示出來。
  • 如下圖所示,內存泄漏檢測工具運行的效果。

Debug調試常用的宏

__FILE__            //所在的文件
__FUNCTION__        //函數功能
__FUNCDNAME__       //函數名
__LINE__            //在第幾行
__DATE__            //產生的日期
__TIME__            //產生的時間

代碼實現

MemoryCheck.h

#pragma once
#ifndef __MEMORY_CHECK_H__
#define __MEMORY_CHECK_H__
#ifdef _DEBUG

void* operator new(size_t size, const char* filename, const char* funame, int line);
void* operator new[](size_t size, const char* filename, const char* funame, int line);
void operator delete(void* pMem);
void operator delete[](void* pMem);

#ifndef __USE_MEM_CHECK__
#define __USE_MEM_CHECK__
#define new new(__FILE__,__FUNCTION__,__LINE__)
#endif // !__USE_MEM_CHECK__
#endif // _DEBUG
#endif // !__MEMORY_CHECK_H__

MemoryCheck.cpp

#include<map>
#include<iostream>
#include<Windows.h>
#define __USE_MEM_CHECK__
#include"MemoryCheck.h"

typedef struct stMemInfo
{
	void* pMem;
	size_t size;
	int line;
	char funcname[256];
	char filename[256];
}MEMINFO,*LPMEMINFO;


std::map<void*, LPMEMINFO>g_MemMap;//存儲內存分配的信息
typedef std::map<void*, LPMEMINFO>MEMMAP;
typedef MEMMAP::iterator MEMMAPItr;

class CMemMgr
{
public:
	static CMemMgr& Instance()
	{
		static CMemMgr instance;
		return instance;
	}

	void* Push(LPMEMINFO pInfo)
	{
		g_MemMap[pInfo->pMem] = pInfo;
		return pInfo->pMem;
	}
	void Pop(void* pMem)
	{
		MEMMAPItr it = g_MemMap.find(pMem);
		if (it != g_MemMap.end())
		{
			free(pMem);
			free(it->second);
			g_MemMap.erase(it);
		}
	}
	~CMemMgr()
	{
		if (!g_MemMap.empty())
		{
			OutputDebugStringA("\n----------------------------------發現內存泄露信息----------------------------------\n\n");
			char buf[256] = {};
			int count = 0;
			for (auto it:g_MemMap)
			{
				sprintf_s(buf, "【內存泄漏警告 %d 】 文件%s,第%d行的函數%s中泄漏了%d個字節的內存\n",
					count++,
					it.second->filename,
					it.second->line,
					it.second->funcname,
					it.second->size);

				OutputDebugStringA(buf);
				free(it.second->pMem);
				free(it.second);
			}
			g_MemMap.clear();
			OutputDebugStringA("\n-------------------------------內存泄漏檢測結束----------------------------------\n\n");
		}
	}
private:
	CMemMgr() {};
	CMemMgr& operator=(const CMemMgr&) = delete;
	CMemMgr(const CMemMgr&) = delete;
};


void* operator new(size_t size, const char* filename, const char* funcname, int line)
{
	LPMEMINFO pInfo=(LPMEMINFO)malloc(sizeof(MEMINFO));
	pInfo->size = size;
	pInfo->line = line;
	pInfo->pMem = malloc(size);
	strcpy_s(pInfo->filename, filename);
	strcpy_s(pInfo->funcname, funcname);
	return CMemMgr::Instance().Push(pInfo);
}
void* operator new[](size_t size, const char* filename, const char* funcname, int line)
{
	return operator new(size, filename, funcname, line);
}
void operator delete(void* pMem)
{
	CMemMgr::Instance().Pop(pMem);
}
void operator delete[](void* pMem)
{
	operator delete(pMem);
}

main.cpp

#include<iostream>
#include"MemoryCheck.h"

int main()
{
	int* p = new int;
	int* p2 = new int[5];
	system("pause");
	return 0;
}

注意

  • 重載的new相沖突解決辦法:
  • 1.使用宏定義開關#define USE_MEM_CHECK,定義在MemoryCheckr.h的前面才能實現宏定義開關,注意順序很重要,順序錯誤也會導致不識別。
  • 2.使用#undef new也可解決


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM