VC++中本身就有内存泄漏检查的机制,可以在向导生成的支持MFC的工程中看到如下代码:
  #ifdef _DEBUG
  #define new DEBUG_NEW
  #undef THIS_FILE
  static char THIS_FILE[] = __FILE__;
  #endif

先具体解释一下:

#ifdef _DEBUG //如果有定义_DEBUG 
#define new DEBUG_NEW //将new宏定义成DEBUG_NEW, 
那么则是代码中有new的都换成DEBUG_NEW。 
#undef THIS_FILE//取消THIS_FILE的宏定义 
static char THIS_FILE[] = __FILE__;//将THIS_FILE 
定义成一个数组,该数组用static声名,则只能在该文件内访问。__FILE__是gcc定义的一个扩展宏,代表的该文件的文件名。 
#endif//这个不用说了吧

MFC Library中的解释
THIS_FILESee Also
MFC Macros and Globals | ASSERT | VERIFYExpands to the name of the file that is being compiled.
THIS_FILE
Remarks
The information is used by the ASSERT and VERIFY macros. The Application Wizard and code wizards place the macro in source code files they create.
Example
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
// __FILE__ is one of the six predefined ANSI C macros that the 
// compiler recognizes.
有了这样的定义,在编译DEBUG版时,出现在这个cpp文件中的所有new都被替换成DEBUG_NEW了。那么DEBUG_NEW是什么呢?DEBUG_NEW也是一个宏,以下摘自afx.h,1632行
 
#define DEBUG_NEW new(THIS_FILE, __LINE__)
 
所以如果有这样一行代码:
 
char* p = new char[200];
 
经过宏替换就变成了:
 
char* p = new( THIS_FILE, __LINE__)char[200];
 
根据C++的标准,对于以上的new的使用方法,编译器会去找这样定义的operator new:
 
void* operator new(size_t, LPCSTR, int)
 
我们在afxmem.cpp 63行找到了一个这样的operator new 的实现
 
void* AFX_CDECL operator new(size_t nSize, LPCSTR lpszFileName, int nLine)
{
       return ::operator new(nSize, _NORMAL_BLOCK, lpszFileName, nLine);
}
 
void* __cdecl operator new(size_t nSize, int nType, LPCSTR lpszFileName, int nLine)
{
       …
              pResult = _malloc_dbg(nSize, nType, lpszFileName, nLine);
              if (pResult != NULL)
                     return pResult;
       …
}
 
第二个operator new函数比较长,为了简单期间,我只摘录了部分。很显然最后的内存分配还是通过_malloc_dbg函数实现的,这个函数属于MS C-Runtime Library 的Debug Function。这个函数不但要求传入内存的大小,另外还有文件名和行号两个参数。文件名和行号就是用来记录此次分配是由哪一段代码造成的。如果这块内存在程序结束之前没有被释放,那么这些信息就会输出到Debug窗口里。
 
这里顺便提一下THIS_FILE,__FILE和__LINE__。__FILE__和__LINE__都是编译器定义的宏。当碰到__FILE__时,编译器会把__FILE__替换成一个字符串,这个字符串就是当前在编译的文件的路径名。当碰到__LINE__时,编译器会把__LINE__替换成一个数字,这个数字就是当前这行代码的行号。在DEBUG_NEW的定义中没有直接使用__FILE__,而是用了THIS_FILE,其目的是为了减小目标文件的大小。假设在某个cpp文件中有100处使用了new,如果直接使用__FILE__,那编译器会产生100个常量字符串,这100个字符串都是这个cpp文件的路径名,显然十分冗余。如果使用THIS_FILE,编译器只会产生一个常量字符串,那100处new的调用使用的都是指向常量字符串的指针。
 
再次观察一下由MFC Application Wizard生成的项目,我们会发现在cpp文件中只对new做了映射,如果你在程序中直接使用malloc函数分配内存,调用malloc的文件名和行号是不会被记录下来的。如果这块内存发生了泄漏,MS C-Runtime Library仍然能检测到,但是当输出这块内存块的信息,不会包含分配它的的文件名和行号。
 
要在非MFC程序中打开内存泄漏的检测功能非常容易,你只要在程序的入口处加入以下几行代码:
 
int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag( tmpFlag );
 
这样,在程序结束的时候,也就是winmain,main或dllmain函数返回之后,如果还有内存块没有释放,它们的信息会被打印到Debug窗口里。
 
如果你试着创建了一个非MFC应用程序,而且在程序的入口处加入了以上代码,并且故意在程序中不释放某些内存块,你会在Debug窗口里看到以下的信息:
 
{47} normal block at 0x00C91C90, 200 bytes long.
 Data: <                > 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
 
内存泄漏的确检测到了,但是和上面MFC程序的例子相比,缺少了文件名和行号。对于一个比较大的程序,没有这些信息,解决问题将变得十分困难。
 
为了能够知道泄漏的内存块是在哪里分配的,你需要实现类似MFC的映射功能,把new,maolloc等函数映射到_malloc_dbg函数上。这里我不再赘述,你可以参考MFC的源代码。
由于Debug Function实现在MS C-RuntimeLibrary中,所以它只能检测到堆内存的泄漏,而且只限于malloc,realloc或strdup等分配的内存,而那些系统资源,比如HANDLE,GDI Object,或是不通过C-Runtime Library分配的内存,比如VARIANT,BSTR的泄漏,它是无法检测到的,这是这种检测法的一个重大的局限性。另外,为了能记录内存块是在哪里分配的,源代码必须相应的配合,这在调试一些老的程序非常麻烦,毕竟修改源代码不是一件省心的事,这是这种检测法的另一个局限性。

关于THIS_FILE的更多相关文章

  1. #ifdef _DEBUG #undef THIS_FILE static char THIS_FILE[]=__FILE__; #endif

    情况1:#ifdef _DEBUGvirtual void AssertValid() const;                 //assert(断言)valid(有效的,正确的)virtual ...

  2. C++编译错误syntax error : identifier 'THIS_FILE' 解决方法

    你到代码里搜索 THIS_FILE看是不是它定义在别的头文件前面了,如果是,把它们的头文件紧跟到Stdafx.h后面 我遇到过这问题,这样搞定的 今天遇到个编译错误:..\vc98\include\n ...

  3. DEBUG_NEW和THIS_FILE

    C++ 的一个 比较晦涩难懂的特点是你可以重载 new 操作符,并且你甚至可以给它附加参数.通常,操作符 new 只接受拟分配对象的大小:        void* operator new(size ...

  4. XMPP iOS客户端实现三:登录、注册

    1.创建一个单例模式来管理xmpp的连接和操作 +(XMPPManager *)share { static XMPPManager *_share=nil; static dispatch_once ...

  5. 摄像头视频捕捉(简单通用--通过IsampleGrabberCB实现)

    前言 DirectShow是微软公司提供的一套在Windows平台上进行流媒体处理的开发包,与DirectX开发包一起发布.DirectShow为多媒体流的捕捉和回放提供了强有力的支持.用Direct ...

  6. CSipSimple 简单分析

    简介 CSipSimple是一款可以在android手机上使用的支持sip的网络电话软件,可以在上面设置使用callda网络电话.连接使用方式最好是使用wifi,或者3g这样上网速度快,打起电话来效果 ...

  7. C++的简单“五子棋”游戏,只是核心代码,资源代码未添加

    ChessBoard.h #ifndef __CHESS_BOARD_H__ #define __CHESS_BOARD_H__ #include "DataStruct.h" # ...

  8. C++ I/O stream

    C++ I/O stream Ø The first, you should understand following: ... Ø Text: Ø Version: Visual studio 20 ...

  9. 使用Python写Windows Service服务程序

    1.背景 如果你想用Python开发Windows程序,并让其开机启动等,就必须写成windows的服务程序Windows Service,用Python来做这个事情必须要借助第三方模块pywin32 ...

随机推荐

  1. Intent传输数据的补充

    发现用intent的putExtra()或者putExtras()传输的都是基本数据类型. 如果要传输自定义数据类型,就要用到其他方法,老罗介绍的大概有3种: 1.  静态变量 2.  全局变量 3. ...

  2. Scrum角色

    产品负责人(Product Owner)的职责如下: 确定产品的功能. 决定发布的日期和发布内容. 为产品的profitability       of the product (ROI)负责. 根据 ...

  3. Windows Azure 虚拟网络配置(Point to Site)

    说明:本文以Azure国际版为例,中国版在网络位置会存在一定差异. 1. 场景 虚拟网络为我们提供了在Windows Azure云计算环境上构建网络定义的能力,通过虚拟网络,我们可以方便地将Windo ...

  4. URAL-1989 Subpalindromes 多项式Hash+树状数组

    题目链接:http://acm.timus.ru/problem.aspx?space=1&num=1989 题意:给出一个字符串,m个操作:1,修改其中一个字符串,2,询问 [a, b] 是 ...

  5. django1.77+mod_wsgi+python2.79+apache2.24 在阿里云centos部署攻略

    心平气和的记录一下今天 踩的坑以防万一 以后还踩 首先我今天的平台是在 阿里云上的一台纯净版的 centos6.5 64位主机上进行的 首先装python2.7 去官网下载python2.7安装包 然 ...

  6. Spark RDD概念学习系列之RDD的依赖关系(宽依赖和窄依赖)(三)

    RDD的依赖关系?   RDD和它依赖的parent RDD(s)的关系有两种不同的类型,即窄依赖(narrow dependency)和宽依赖(wide dependency). 1)窄依赖指的是每 ...

  7. UVALive 3956 Key Task (bfs+状态压缩)

    Key Task 题目链接: http://acm.hust.edu.cn/vjudge/contest/129733#problem/D Description The Czech Technica ...

  8. Struts ForwardAction Example

    In Struts MVC model, you have to go thought the Action Controller to get a new view page. In some ca ...

  9. FATFS文件系统

    STM32移植文件系统,操作SD卡,对SD卡进行读写 FATFS文件系统与底层介质的驱动分离开来,对底层介质的操作都要交给用户去实现,它仅仅是提供了一个函数接口而已,函数为空,要用户添加代码.然后 F ...

  10. USB2.0规范

    USB是一种支持热插拔的高速串行传输总线,它使用差分信号来传输数据,最高速度可达480Mb/S. USB支持“总线供电”和“自供电”两种供电模式.在总线供电模式下,设备最多可以获得500mA的电流.U ...