关于 c++ 内存泄漏

        对于windows 32为系统来说,一般一个进程的内存有4Gb,当然这是虚拟内存,windows 用一堆的数据结构来保存 进程,线程,虚拟内存。。。。 在4GB的虚拟内存中,需要映射高2G的内存给操作系统(通过开关设置,可以只配置1G的内存给操作系统),所以对于进程来说,就只有2Gb的虚拟内存,当一个进程被加载的时候,进程的可执行文件需要占用一定的虚拟内存,被加载进来的模块(dll 动态链接库,其实对于windows而言,就是pe文件).也会占用进程虚拟空间。当一个进程被加载的时候,操作系统会分配1MB的内存给进程作为默认堆,当你通过malloc 来分配时,就是从这个默认堆中分配的,因为,默认堆是所有线程共用的,所以分配的时候一定会存在锁,所以分配存在竞争。当然,也不是一定1MB啦,因为我们一般是从main函数开始写,但像我们的编译器会在之前增加很多的代码,默认堆的分配也是编译器做的。你也可以通过编译器设置。我们还可以通过virtualalloc分配一些私有堆,然后写一个分配器,来自己管理内存的分配。每个线程一般编译器会配置1MB的栈,实际先提交4KB。通过页保护来增长栈。但是每个线程只有1MB的栈,并不是无线增长的。ok,浅浅的回忆了一下。有时间,要好好总结一下。
        对于c++而言,内存泄漏是不可避免,但可以采取一些防御手段。
       代码1 可以帮我们干掉一些比较浅显的内存泄漏,避免花时间去找这些bug.
       代码1 的内存分配 必须是在类对生存时间里,当该类的对象被释放的时候,该对象里的分配的内存也必须释放的情况。ok,上代码。
       代码1:
commonMacro.h  
#pragma once

#define IS_EQUAL(lVal,rVal)		((lVal) == (rVal))			// equal
#define NOT_EQUAL(lVal,rVal)	        ((lVal) != (rVal))			// not equal	
#define IS_TRUE(isTrue)			IS_EQUAL(true , isTrue)			// is true
#define NOT_TRUE(notTrue)		NOT_EQUAL(true , notTrue)		// not true
#define IS_FALSE(isFalse)		IS_EQUAL(false , isFalse)		// is false
#define NOT_FALSE(notFalse)		NOT_EQUAL(false , notFalse)		// not false
#define IS_NULLPTR(ptr)			IS_EQUAL(nullptr , ptr)			// is null
#define NOT_NULLPTR(ptr)		NOT_EQUAL(nullptr , ptr)		// not null
commonMacro.h 是我比较常用的宏,因为,我不想花时间去处理   if( i = 1 ){  xxxx }  这种bug

controlOut.h controlOut.cpp 控制台输出
#pragma once

#include "commonMacro.h"

#include <wtypes.h>
#include <memory>

#define CrlOutA(...)	do{ Console::check(); printf(__VA_ARGS__); printf("\n");}while(0)
#define CrlOutW(...)	do{ Console::check(); wprintf(__VA_ARGS__); printf("\n");}while(0)

#ifdef _UNICODE
#define CrlOut	CrlOutW;
#else
#define CrlOut	CrlOutA;
#endif

class Console
{
public:
	static void  check();

private:
	Console();
	~Console();
	static BOOL _isConsoleProgram();
	static void _initConsoleWindow();
	int _init();
	friend class std::auto_ptr<Console>;

	class Lock
	{
	public:
		Lock(){ InitializeCriticalSection(&m_cs); }
		~Lock(){ DeleteCriticalSection(&m_cs); }
		void upLock() { EnterCriticalSection(&m_cs); }
		void unLock() { LeaveCriticalSection(&m_cs); }
	private:
		CRITICAL_SECTION			m_cs;
	};

private:
	static std::auto_ptr<Console>	m_instance;
	BOOL							m_bIsConPro;
	static Lock						m_lock;
};
#include "controlOut.h"
#include <io.h>
#include <stdio.h>
#include <fcntl.h>

Console::Lock Console::m_lock;

std::auto_ptr<Console> Console::m_instance;

void Console::check()
{
	if (IS_NULLPTR(m_instance.get()))
	{
		m_lock.upLock();
		if (IS_NULLPTR(m_instance.get()))
			m_instance.reset(new Console);
		m_lock.unLock();
	}
}

Console::Console()
{
	_init();
};

Console::~Console()
{
	if (IS_FALSE(m_bIsConPro))
		FreeConsole();
}


BOOL Console::_isConsoleProgram()
{
	auto pDosH = (PIMAGE_DOS_HEADER)GetModuleHandle(nullptr);
	auto pNtH = (PIMAGE_NT_HEADERS)
		((LONG)pDosH + pDosH->e_lfanew);
	WORD Subsystem = pNtH->OptionalHeader.Subsystem;
	if (IS_EQUAL(IMAGE_SUBSYSTEM_WINDOWS_CUI, Subsystem) ||
		IS_EQUAL(IMAGE_SUBSYSTEM_OS2_CUI, Subsystem) ||
		IS_EQUAL(IMAGE_SUBSYSTEM_POSIX_CUI, Subsystem))
		return TRUE;
	return FALSE;
}

void Console::_initConsoleWindow()
{
	AllocConsole();
	int nCrt = _open_osfhandle(
		(long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);
	FILE* fp = _fdopen(nCrt, "w");
	*stdout = *fp;
	setvbuf(stdout, nullptr, _IONBF, 0);
}

int Console::_init()
{
	int nRet = 0;

	if (IS_FALSE(_isConsoleProgram()))
	{
		_initConsoleWindow();
		m_bIsConPro = FALSE;
		goto _exit;
	}
	m_bIsConPro = TRUE;
_exit:
	SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);
	return nRet;
}
控制台输出,是我比较常用的 调试必备代码。适合在GUI程序中弹出控制台输出,用单例,主要是避免在多线程中,多次启动控制台。

接着,就是主题了,采用宏,是为了方便类的使用。模板,慢慢品味吧。看不懂的话,熟悉一下c++11特性。分析一下vs中stl源码。
/*
*   author: zyb
*   \brief  memory  safe malloc  
*/

#pragma once

#include <map>

#include "commonMacro.h"
#include "controlOut.h"

#ifdef _DEBUG

#include <assert.h>

#define __DIGIT2STR(a)		#a
#define _DIGIT2STR(a)		__DIGIT2STR(a)
#define _SPACE_STR			" "
#define _ADDR2INT(a)		reinterpret_cast<int>(a)
#define _CALLPOS			__FUNCTION__						\
							_SPACE_STR							\
							_DIGIT2STR(__LINE__)

#define CALLPOS				_CALLPOS,sizeof(_CALLPOS)

#define NEW_COMMON(pObj,pszPos,iPosLen,m_map)					\
	char* pInfo = new char[iPosLen];							\
	strcpy_s(pInfo,iPosLen,pszPos);								\
	m_map.insert(pair_t(_ADDR2INT(pObj),pInfo));				\
	return pObj;

#define DELETE_COMMON(pObj,m_map)								\
	if(IS_NULLPTR(pObj))										\
		return;													\
	memcheck_iterator_t it =									\
			m_map.find(_ADDR2INT(pObj));						\
	assert(NOT_EQUAL(it,m_map.end()));							\
	char* pInfo = it->second;									\
	m_map.erase(it);											\
	delete[] pInfo;	

#define DECLARE_MEMCHECK_MEMBER(classname)						\
private:														\
typedef std::map<int,char*>	memcheck_store_t;					\
typedef memcheck_store_t::iterator memcheck_iterator_t;			\
typedef std::pair<int,char*> pair_t;							\
																\
memcheck_store_t m_##classname##_map;							\
																\
template<class _Ty,class... _Types>								\
_Ty* Safnew(char* pszPos,int iPosLen,_Types... params)			\
{																\
	_Ty* pObj = new _Ty(params...);								\
	NEW_COMMON(pObj,pszPos,iPosLen,m_##classname##_map);		\
}																\
																\
template<class _Ty>												\
_Ty* Safnew_a(char* pszPos,int iPosLen,int aLen)				\
{																\
	_Ty* pObj = new _Ty[aLen];									\
	NEW_COMMON(pObj,pszPos,iPosLen,m_##classname##_map);		\
}																\
																\
template<class _Ty>												\
void Safdelete(_Ty& pObj)										\
{																\
	DELETE_COMMON(pObj,m_##classname##_map)						\
	delete pObj;												\
	pObj = nullptr;												\
																\
	return;														\
}																\
template<class _Ty>												\
void Safedelete_a(_Ty& pObj)									\
{																\
	DELETE_COMMON(pObj,m_##classname##_map)						\
	delete[] pObj;												\
	pObj = nullptr;												\
																\
	return;														\
}

#define END_CHECKMEM(classname)									\
	do{															\
		if(IS_TRUE(												\
			m_##classname##_map.empty()))						\
			break;												\
		memcheck_iterator_t it_start =							\
			m_##classname##_map.begin();						\
		memcheck_iterator_t it_end =							\
			m_##classname##_map.end();							\
		for(;it_start != it_end;it_start++)						\
		{														\
			CrlOutA("%s",it_start->second);						\
			m_##classname##_map.erase(it_start);				\
			delete[] it_start->second;							\
		}														\
		assert(0);												\
	}while(0)

#else

#define CALLPOS				nullptr,0

// VS's optimize was very powerful
#define DECLARE_MEMCHECK_MEMBER(classname)						\
private:														\
template<class _Ty,class... _Types>								\
inline _Ty* Safnew(char* pszPos, int iPosLen,_Types... params)	\
{																\
	UNREFERENCED_PARAMETER(pszPos);								\
	UNREFERENCED_PARAMETER(iPosLen);							\
	return new _Ty(params...);									\
}																\
																\
template<class _Ty>												\
inline _Ty* Safnew_a(char* pszPos,int iPosLen,int aLen)			\
{																\
	UNREFERENCED_PARAMETER(pszPos);								\
	UNREFERENCED_PARAMETER(iPosLen);							\
	return new _Ty[aLen];										\
}																\
																\
template<class _Ty>												\
inline void Safdelete(_Ty& pObj)								\
{																\
	if(IS_NULLPTR(pObj))										\
		return;													\
	delete pObj;												\
	pObj = nullptr;												\
}																\
																\
template<class _Ty>												\
inline void Safdelete_a(_Ty& pObj)								\
{																\
	if(IS_NULLPTR(pObj))										\
		return;													\
	delete[] pObj;												\
	pObj = nullptr;												\
}

#define END_CHECKMEM(classname)

#endif //_DEBUG

原理 包装new delete函数。在分配的时候,记录函数的名字记行号,释放的移除,最后当结束的时候,在检查时候释放完毕。只能在 debug上进行内存追踪。在release上,VS会去优化代码。

演示代码:
#include "MemCheck.h"

class pig
{
	DECLARE_MEMCHECK_MEMBER(pig);
public:
	pig(const char* pszName = "name_pig");
	~pig();
	void name()
	{
		printf("%s\n", m_pName->c_str());
	}
private:
	std::string*	m_pName;
};

pig::pig(const char* pszName /* = "name_pig" */)
{
	m_pName = Safnew<std::string>(CALLPOS,pszName);
}
pig::~pig()
{
//	Safdelete(m_pName);			//未释放
	END_CHECKMEM(pig);
}

class dog
{
	DECLARE_MEMCHECK_MEMBER(dog);
public:
	dog(const char* pszName = "name_dog");
	~dog();
	void name()
	{
		int cchar = m_pName->length();
		char* ptempname = Safnew_a1<char>(CALLPOS, cchar+1);		//局部分配,未释放
		memcpy(ptempname, m_pName->c_str(), cchar);
		ptempname[cchar] = 0;
		printf("%s\n", ptempname);
	}
private:
	std::string*	m_pName;
};

dog::dog(const char* pszName /* = "name_dog" */)
{
	m_pName = Safnew<std::string>(CALLPOS, pszName);
}
dog::~dog()
{
	Safdelete(m_pName);
	END_CHECKMEM(dog);					//检测内存
}

class cat
{
	DECLARE_MEMCHECK_MEMBER(cat);
public:
	cat(const char* pname = "name_cat");
	~cat();
	char* getCatName()
	{
		int cchar = m_pName->length();
		char* ptempname = new char[cchar + 1];//Safnew_a1<char>(CALLPOS, cchar + 1);//在这里不适合使用Safnew_al  因为分配的内                                                                                            //存被其他类使用
		memcpy(ptempname, m_pName->c_str(), cchar);
		ptempname[cchar] = 0;
		return ptempname;											
	}
private:
	std::string* m_pName;
};

cat::cat(const char* pname /* = "name_cat" */)
{
	m_pName = Safnew<std::string>(CALLPOS, pname);
}

cat::~cat()
{
	Safdelete(m_pName);
	END_CHECKMEM(cat);
}

class Zoo
{
	DECLARE_MEMCHECK_MEMBER(Zoo);
public:
	Zoo(const char* pigname = "name_pig",
		const char* dogname = "name_dog",
		const char* catname = "name_cat");
	~Zoo();
	void name()
	{
		m_pPig->name();
		m_pDog->name();
		const char* cat_name = m_pCat->getCatName();
		printf("%s\n", cat_name);
		delete[] cat_name;								//外部类释放
	}
public:
	pig *m_pPig;
	dog *m_pDog;
	cat *m_pCat;
};

Zoo::Zoo(const char* pigname /* = "name_pig" */,
	const char* dogname /* = "name_dog" */,
	const char* catname /* = "name_cat" */)
{
	m_pPig = Safnew<pig>(CALLPOS, pigname);
	m_pDog = Safnew<dog>(CALLPOS, dogname);
	m_pCat = Safnew<cat>(CALLPOS, catname);
}

Zoo::~Zoo()
{
	Safdelete(m_pPig);
	Safdelete(m_pDog);
	END_CHECKMEM(Zoo);
}


void main()
{
	Zoo test; 
	test.name();
}

ok ,这就是代码1了 ,不知道为什么,不太想解释太多。明天周末了,晚上去玩一晚英雄联盟。不知道什么时候开始了这个习惯。 i just a 程序员

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值