在用VC++开发COM组件时,某些类实例没有被析构,从而导致内存泄露。即使类实例最终被析构,但没有在运行中按期望及时的析构,也说明程序设计存在缺陷。我们当然可以在每个类的析构函数中设置一个断点,实时跟踪实例的析构情况。但如果用这种方法对大量的类实例进行跟踪,就显得太繁琐了。
因此,我们可以设计一个机制,能自动跟踪类实例的创建和析构动作,并将统计信息实时的呈现出来(如写入IDE的output窗口,这样不会影响程序的正常运行)。这样,我们就可以定位问题出现在哪个类中,然后可以作进一步的分析。
下面,我们实现一个“类实例跟踪统计管理器”,包含在一个文件"TraceClass.h"中,可以按如下的方式呈现统计结果。
(
分两行
)
********* Total Class1 Class2 Class3
4
3
1
0
使用方法也很简单:
- 当然是先包含头文件"TraceClass.h"
- 在类(假设类名为Class1)中加入一行 _TRACE_CLASS(Class1) 即可。
源码如下:也可直接下载:http://download1.youkuaiyun.com/down3/20070522/22175433588.h
#pragma
once


/**/
/* 类实例跟踪统计管理器 TraceClass.h
作者: pimshell
博客: http://blog.youkuaiyun.com/pimshell
更新地址:http://blog.youkuaiyun.com/pimshell/archive/2007/05/22/1621254.aspx

当前版本: Version 1.0 - 05/22/2007

用途:
1、debug时,用于跟踪类实例的创建与销毁动作,统计类实例的个数,
并将统计结果写入Output窗口。
从而协助检测内存泄露情况。
2、release时,本跟踪统计代码不被编译。

统计结果格式:
(分两行)
********* Total Class1 Class2 Class3
4 3 1 0

使用举例:
class Class1
{
_TRACE_CLASS(Class1) //只需这一行代码
};

//(可选)
#define _TRACECLASS_MINWIDTH 10 //每个单元格最小的宽度
#define _TRACECLASS_PREFIX _T("~~~~~") //第一行的前缀

期望:
为了更好的完善代码,如有好的建议请上博客回复,
也可以随时从“更新地址”下载最新的版本。

更新地址:http://blog.youkuaiyun.com/pimshell/archive/2007/05/22/1621254.aspx

版本修改历史:

1.0: - 初始版本

*/
#ifdef _DEBUG

#ifndef _TRACECLASS_MINWIDTH
#define
_TRACECLASS_MINWIDTH 8
#endif

#ifndef _TRACECLASS_PREFIX
#define
_TRACECLASS_PREFIX _T("*******")
#endif

//
trace class manager
class
CTraceClassManager

...
{
public:
private:
CAtlMap<CString,LONG> m_oTraceMap;
CComAutoCriticalSection m_oTraceCS;

private:
inline void lock()

...{
m_oTraceCS.Lock();
}

inline void unlock()

...{
m_oTraceCS.Unlock();
}

private:
void trace()

...{
CString sOne,sTwo;
CString sLineOne,sLineTwo;

//
long nRefTotal=0;
POSITION pos=m_oTraceMap.GetStartPosition();
while(pos!=NULL)

...{
//next
CString sClassName;
LONG nRef;
m_oTraceMap.GetNextAssoc(pos,sClassName,nRef);

//trace
getTraceString(sClassName,nRef,sOne,sTwo);
sLineOne+=sOne;
sLineTwo+=sTwo;

//ref
nRefTotal+=nRef;
}
//total
getTraceString(_T("Total"),nRefTotal,sOne,sTwo);
sLineOne=sOne+sLineOne;
sLineTwo=sTwo+sLineTwo;

//prefix
getTraceString(_TRACECLASS_PREFIX,-1,sOne,sTwo);
sLineOne=sOne+sLineOne;
sLineTwo=sTwo+sLineTwo;

//rn
sLineOne+=_T(" ");
sLineTwo+=_T(" ");

//trace
ATLTRACE(sLineOne);
ATLTRACE(sLineTwo);
}

void getTraceString(LPCTSTR szName,long nRef,CString& sOne,CString& sTwo)

...{
//count
long nCount=(long)wcslen(szName)+1;
if(nCount<_TRACECLASS_MINWIDTH)
nCount=_TRACECLASS_MINWIDTH;
//one
CString sFormat;
sFormat.Format(_T("%%%ds"),nCount);
sOne.Format(sFormat,szName);
//linetwo
if(nRef==-1)

...{
sTwo.Format(sFormat,L"");
}
else

...{
sFormat.Format(_T("%%%dd"),nCount);
sTwo.Format(sFormat,nRef);
}
}

public:

//add class
void addClass(LPCTSTR szClassName)

...{
this->lock();

//set
POSITION pos=m_oTraceMap.Lookup(szClassName);
if(pos==NULL)

...{
m_oTraceMap.SetAt(szClassName,1);
}
else

...{
LONG nRef=m_oTraceMap.GetValueAt(pos);
m_oTraceMap.SetValueAt(pos,++nRef);
}

//trace
this->trace();

this->unlock();
}

//remove class
void removeClass(LPCTSTR szClassName)

...{
this->lock();

//set
POSITION pos=m_oTraceMap.Lookup(szClassName);
if(pos!=NULL)

...{
//if ref==0 neednot remove it.
LONG nRef=m_oTraceMap.GetValueAt(pos);
m_oTraceMap.SetAt(szClassName,--nRef);
}

//trace
this->trace();

this->unlock();
}
}
;

//
global member of trace class manager
__declspec( selectany ) CTraceClassManager g_oTraceClassManager;

//
trace class
template
<
class
T
>
class
CTraceClass

...
{
public:
CTraceClass(void)

...{
g_oTraceClassManager.addClass( T::_GetTraceClassName() );
}

public:
~CTraceClass(void)

...{
g_oTraceClassManager.removeClass( T::_GetTraceClassName() );
}
}
;

#endif

//
macro
#ifdef _DEBUG
#define
_TRACE_CLASS(T)
private
:
CTraceClass
<
T
>
_oTraceClass;
public
:
static
CString _GetTraceClassName()

...
{
USES_CONVERSION;
return CString(A2T(#T));
}

#else
#define
_TRACE_CLASS(T)
#endif