zoukankan      html  css  js  c++  java
  • VC++6.0中内存泄漏检测

    这篇文章是对2004-09-02日发表的《VC++6.0中简单的内存泄漏检测事例代码》(已经删除)的更新.

    对C++代码而言,内存泄漏问题虽然有诸多方法避免,但实际代码编写的时候,或出于自信或出于复杂性的考虑,常常还会用到原始的operator new,这不可避免的会带来内存泄漏的可能,不久前本人因为违反了"可用于被多态继承的基类其析构函数应当有virtual修饰"的法则( 一不小心就忘了写virtual ^_^ ),导致了内存泄漏,因此我觉得出于安全考虑,在代码中加入内存泄漏检查机制还是很必要的,也因为这次的内存泄漏事件促使我写出这一篇文章.

    VC++中本身就有内存泄漏检查的机制,你可以在向导生成的支持MFC的工程中看到如下代码:
      #ifdef _DEBUG
      #define new DEBUG_NEW
      #undef THIS_FILE
      static char THIS_FILE[] = __FILE__;
      #endif
    通过它们,你能非常容易的发现代码中的内存泄漏,但是如果手工将这个功能移植到非MFC工程中去是很繁琐的一件事,另外它还有一个bug,在多线程并发调用这个DEBUG_NEW时会导致系统级错误,因此本人在此重写了这个功能,将以下的debug_new.h和debug_new.cpp添加到工程中,并在需要检测的cpp中#include "debug_new.h"和main中一开始处加入REG_DEBUG_NEW宏即可.

    1. debug_new.h 源代码

    /************************************************************************/
    /* comment:  此文件与debug_new.cpp配合使用,用于在调试期发现内存泄漏     */
    /*           仅在VC++编译器中适用(包括Intel C++,因为它使用了相同的库)   */
    /* 作者:     周星星 http://blog.vckbase.com/bruceteen/                  */
    /* 版权申明: 无,可任意 使用,修改 和 发布                                */
    /************************************************************************/

    /* sample

    #include <iostream>
    #include "debug_new.h" // +
    using namespace std;

    int main( void )
    {
        REG_DEBUG_NEW; // +

        char* p = new char[2];

        cout << "--End--" << endl;
        return 0;
    }

    在VC++ IDE中按F5调试运行将会在Output窗口的Debug页看到类似如下的提示:
    Dumping objects ->
    d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
    Data: <  > CD CD
    Object dump complete.

    如果不出现如上提示请Rebuild All一次.

    */


    #ifndef _DEBUG_NEW_H_
    #define _DEBUG_NEW_H_

        #ifdef _DEBUG

            #undef new
            extern void _RegDebugNew( void );
            extern void* __cdecl operator new( size_t, const char*, int );
            extern void __cdecl operator delete( void*, const char*, int);
            #define new new(__FILE__, __LINE__)
            
            #define REG_DEBUG_NEW _RegDebugNew();

        #else

            #define REG_DEBUG_NEW

        #endif // _DEBUG

    #endif // _DEBUG_NEW_H_


    2. debug_new.cpp 源代码
    /************************************************************************/
    /* comment:  此文件与debug_new.h配合使用,用于在调试期发现内存泄漏       */
    /*           仅在VC++编译器中适用(包括Intel C++,因为它使用了相同的库)   */
    /* 作者:     周星星 http://blog.vckbase.com/bruceteen/                  */
    /* 版权申明: 无,可任意 使用,修改 和 发布                                */
    /************************************************************************/

    //#include "debug_new.h"

    #ifdef _DEBUG

    #include <windows.h>
    #include <crtdbg.h>

    class _CriSec
    {
        CRITICAL_SECTION criSection;
    public:
        _CriSec()    { InitializeCriticalSection( &criSection ); }
        ~_CriSec()   { DeleteCriticalSection( &criSection );     }
        void Enter() { EnterCriticalSection( &criSection );      }
        void Leave() { LeaveCriticalSection( &criSection );      }
    } _cs;

    void _RegDebugNew( void )
    {
        _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG | _CRTDBG_LEAK_CHECK_DF );
    }
    void* __cdecl operator new( size_t nSize, const char* lpszFileName, int nLine )
    {
        // comment 1: MFC中提供的debug new虽然加了锁,但我在实际测试的时候发现多线程并发
        //            调用的时候还是抛出了系统错误,所以我在这里加了一个线程互斥量.
        // comment 2: debug new和debug delete之间需不需要互斥我并不知道,保险起见,我同样
        //            加了线程互斥量.
        // comment 3: 按照C++标准规定,在operator new失败后应当调用set_new_handler设置的
        //            函数,但是MSDN中却说"头文件new中的set_new_handler是stub的,而应该使
        //            用头文件new.h中的_set_new_handler",这简直是滑天下之大稽.
        //            以下是VC++6.0中的set_new_handler定义:
        //                new_handler __cdecl set_new_handler( new_handler new_p )
        //                {
        //                    assert( new_p == 0 ); // cannot use stub to register a new handler
        //                    _set_new_handler( 0 );
        //                    return 0;
        //                }
        //            所以我也无计可施,只能舍弃set_new_handler的作用.

        _cs.Enter();
        void* p = _malloc_dbg( nSize, _NORMAL_BLOCK, lpszFileName, nLine );
        _cs.Leave();
        return p;
    }
    void __cdecl operator delete( void* p, const char* /*lpszFileName*/, int /*nLine*/ )
    {
        _cs.Enter();
        _free_dbg( p, _CLIENT_BLOCK );
        _cs.Leave();
    }

    #endif


    3. 事例代码
    #include <iostream>
    #include "debug_new.h"
    using namespace std;

    int main( void )
    {
        REG_DEBUG_NEW;

        char* p = new char[2];
        p[0] = 'A';
        p[1] = 'B';

        cout << "--End--" << endl;
        return 0;
    }


    4. 结果输出
    在VC++ IDE中按F5调试运行将会在Output窗口的Debug页看到类似如下的提示:
    ……
    Dumping objects ->
    d:/test.cpp(10) : {45} normal block at 0x003410C8, 2 bytes long.
    Data: <AB> 41 42
    Object dump complete.
    …… 
  • 相关阅读:
    LDAP 总结
    关于OpenLDAPAdmin管理页面提示“This base cannot be created with PLA“问题. Strong Authentication Required问题
    PHP 7.0 5.6 下安裝 phpLDAPadmin 发生错误的修正方法
    ldap、additional info: no global superior knowledge
    ldap安装配置过程中遇到的错误,以及解决方法
    转: LDAP有啥子用? 用户认证
    Mac 安装 brew
    go test 单元函数测试
    haproxy httpcheck with basic auth
    architecture and business process modelling
  • 原文地址:https://www.cnblogs.com/tyjsjl/p/2156076.html
Copyright © 2011-2022 走看看