内存泄露检测与重载new/delete冲突

老邓 2009-11-30 09:35:17
如果A类不重载new/delete的话,MemoryLeakChecker类检测内存泄露的效果还是非常好的!
可是,由于采取了:
#ifdef _DEBUG
#define new DEBUG_NEW
#endif
的方式,导致重载new/delete时出错:
Main.cpp(47) : error C2059: syntax error : 'constant'
Main.cpp(48) : error C2091: function returns function
Main.cpp(48) : error C2802: static member 'operator new' has no formal parameters
Main.cpp(48) : error C2333: 'A::operator new' : error in function declaration; skipping function body
Main.cpp(61) : error C2660: 'A::operator new' : function does not take 4 arguments


我的问题:
问题一:如何解决这个宏与重载的new冲突的问题?如果没有更好的解决方案,请看问题二;
问题二:设计一个类,记录内存申请的地址、文件、行数,在程序退出时写log来查内存泄露?

#include <iostream>
#include <string>

using namespace std;

#ifdef _DEBUG
#define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_NEW
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class MemoryLeakChecker
{
public:
MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
#endif // _DEBUG
}
~MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif // _DEBUG
}
static MemoryLeakChecker& GetInstance()
{
static MemoryLeakChecker checker;
return checker;
}
};

class A
{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }

void* operator new(size_t size)
{
return ::malloc(size);
}

void operator delete(void* p)
{
::free(p);
}
};

int main()
{
MemoryLeakChecker::GetInstance();
A* p = new A;
delete p;
return 0;
}
...全文
1005 42 打赏 收藏 转发到动态 举报
写回复
用AI写文章
42 条回复
切换为时间正序
请发表友善的回复…
发表回复
fxjhot123 2010-07-29
  • 打赏
  • 举报
回复
请问能提供一下 linux下的实现代码么?
不用crtdgb.h头文件
怎么实现内存分配失败时文件名,行号的输出
老邓 2009-12-02
  • 打赏
  • 举报
回复
这是一个比较完善的版本,解决了Debug和Release,以及W4警告问题。
#include <iostream>
#include <string>

using namespace std;

#ifdef _DEBUG
#define DEBUG_NEW new(_NORMAL_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_NEW
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class MemoryLeakChecker
{
public:
MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
#endif // _DEBUG
}
~MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif // _DEBUG
}
static MemoryLeakChecker& GetInstance()
{
static MemoryLeakChecker checker;
return checker;
}
};

class A
{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }

#ifdef _DEBUG
#define DEBUG_NEW_PARAMS , int type, const char* file, int line
#define DEBUG_DELETE_PARAMS , int, const char*, int
#else
#define DEBUG_NEW_PARAMS
#define DEBUG_DELETE_PARAMS
#endif
#pragma push_macro("new")
#undef new
static void* operator new(size_t size DEBUG_NEW_PARAMS)
{
return _malloc_dbg(size, type, file, line);
}
#pragma pop_macro("new")

#ifdef _DEBUG
static void operator delete(void* p DEBUG_DELETE_PARAMS)
{
_free_dbg(p, _NORMAL_BLOCK);
}
#endif

static void operator delete(void* p)
{
_free_dbg(p, _NORMAL_BLOCK);
}
};

int main()
{
MemoryLeakChecker::GetInstance();
A* p = new A;
return 0;
}
老邓 2009-12-02
  • 打赏
  • 举报
回复
区别大了。
我这样实现的话,就可以在类外任意处new了。
当然,如果你那样写,然后使用这样的重载,也是可以通过的。

    static void* operator new(size_t size, int type = _NORMAL_BLOCK, const char* file = __FILE__, int line = __LINE__)
{
return _malloc_dbg(size, type, file, line);
}

static void operator delete(void* p, int /*type*/, const char* /*file*/, int /*line*/)
{
_free_dbg(p, _NORMAL_BLOCK);
}

static void operator delete(void* p)
{
_free_dbg(p, _NORMAL_BLOCK);
}
老邓 2009-12-02
  • 打赏
  • 举报
回复
你是没有测试,你那样写的话,在使用的时候,也需要那样写一次。
你可以跑跑看。
healer_kx 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 35 楼 loaden 的回复:]
总结:
在需要重载new的类定义之前:
C/C++ code#pragma push_macro("new")#undef new
类定义之后:
C/C++ code#pragma pop_macro("new")
就搞定了。
上面代码成功的将行数传递到了重载的new中,输出:
Main.cpp71A
~A
[/Quote]
你这么搞,和我那么写,有啥本质区别啊? 少写了两行而已。。。
老邓 2009-12-01
  • 打赏
  • 举报
回复
如果要实现重载的版本也可以实现内存泄露检测,可以这样:
class A
{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }

static void* operator new(size_t size, int type = _NORMAL_BLOCK, const char* file = __FILE__, int line = __LINE__)
{
return _malloc_dbg(size, type, file, line);
}

static void operator delete(void* p, int /*type*/, const char* /*file*/, int /*line*/)
{
_free_dbg(p, _NORMAL_BLOCK);
}

static void operator delete(void* p)
{
_free_dbg(p, _NORMAL_BLOCK);
}
};

这样,当在Main.cpp的70行申请内存而没有释放时,会输出:
1 23:55:00:718 1212: CmdTest Detected memory leaks!
2 23:55:00:718 1212: CmdTest Dumping objects ->
3 23:55:00:718 1212: CmdTest Main.cpp(70) :
4 23:55:00:718 1212: CmdTest {76}
5 23:55:00:718 1212: CmdTest normal block at 0x00382EC0, 1 bytes long.
6 23:55:00:718 1212: CmdTest Data: < > CD
7 23:55:00:718 1212: CmdTest Object dump complete.
老邓 2009-12-01
  • 打赏
  • 举报
回复
总结:
在需要重载new的类定义之前:
#pragma push_macro("new")
#undef new

类定义之后:
#pragma pop_macro("new")

就搞定了。
上面代码成功的将行数传递到了重载的new中,输出:
Main.cpp71A
~A
老邓 2009-12-01
  • 打赏
  • 举报
回复
嗯,自己搞定了。
不过,还需要继续完善一下!
#include <iostream>
#include <string>

using namespace std;

#ifdef _DEBUG
#define DEBUG_NEW new(_CLIENT_BLOCK, __FILE__, __LINE__)
#else
#define DEBUG_NEW
#endif
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

class MemoryLeakChecker
{
public:
MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
#endif // _DEBUG
}
~MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif // _DEBUG
}
static MemoryLeakChecker& GetInstance()
{
static MemoryLeakChecker checker;
return checker;
}
};

#pragma push_macro("new")
#undef new
class A
{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }

static void* operator new(size_t size, int type = _CLIENT_BLOCK, const char* file = __FILE__, int line = __LINE__)
{
// cout << file << line;
return ::malloc(size);
}

static void operator delete(void* p, int type, const char* file, int line)
{
::free(p);
}

static void operator delete(void* p)
{
::free(p);
}
};

#pragma pop_macro("new")

int main()
{
MemoryLeakChecker::GetInstance();
A* p = new A;
delete p;
return 0;
}
jackzhhuang 2009-12-01
  • 打赏
  • 举报
回复
内存管理问题,要是我就写一个policy模式统统自动化释放,省得我操心。
老邓 2009-12-01
  • 打赏
  • 举报
回复
顶一下...
taodm 2009-12-01
  • 打赏
  • 举报
回复
基于简单的_CrtSetReportMode函数族来实现内存检测本来就是不靠谱的。
老邓 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 lekonpeng 的回复:]
To loaden:
        我这里有个个案,暂时抛开你的两个问题,来看看能不能工作。
        有个程序,由exe,一个Main DLL, 7、8个子DLL,和一Lib组成。
        exe 为框架
        MainDLL 负责其他DLL的管理,插件形式
        Lib 主要是工程的数据结构部分,各种数据对象在这里定义和实现。
        除了exe,那些DLL Link 的时候都要用到LIb。Lib里面的对象都在Lib里面产生。
       
        我想请问在Main dll 入口中加MemoryLeakChecker,能check到整个程序的内存情况吗?框架的除外。  好像内存泄露的检测如果都在一个模块中,检测就还好点,可是如果涉及到N个模块的话就复杂多了
[/Quote]
多模块的我暂时未考虑。
由于不同模块,在静态编译时,都有自己的CRT,所以,可能需要每个模块单独检测...
动态编译(Md)的话,应该可以。
你试试。
老邓 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 23 楼 taodm 的回复:]
换个好点的商用内存泄漏检测工具。
[/Quote]
我的开发工具都是免费的...^_^

忙了一个多小时,测试通过了,大家帮忙提提改进意见吧。

功能描述:
可记录下通过qp开头的宏所申请的内存及删除的内存。
为了以防万一,又加上了MS的内存泄露(不要所在文件、行号)。
例如:
class A
{
public:
A() { cout << "A" << endl; }
~A() { cout << "~A" << endl; }

public:
void* operator new(size_t size)
{
qpMallocNew(p, size);
return p;
}

void operator delete(void* p)
{
qpFreeDelete(p);
}
};

int main()
{
A* p = new A;
qpNewPtrEx(pi, int);
qpMallocEx(pm, float, 1);
qpNewArrayEx(pa, char, 100);

return 0;
}

这将打印出内存泄露报告:MemoryReport.log:
1: Main.cpp, 39, 3
2: Main.cpp, 25, 4
3: Main.cpp, 38, 1
4: Main.cpp, 40, 2


头文件:
/*
* Copyright (C) QPSOFT.COM All rights reserved.
*/

#pragma once

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

class MemoryLeakChecker
{
public:
MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG);
#endif // _DEBUG
}
~MemoryLeakChecker()
{
#ifdef _DEBUG
_CrtDumpMemoryLeaks();
#endif // _DEBUG
}
static MemoryLeakChecker& GetInstance()
{
static MemoryLeakChecker checker;
return checker;
}
};

#ifdef _DEBUG
#define NEWCNT(PTR, TYPE) qp::Memory::GetInstance().New(PTR, __FILE__, __LINE__, TYPE)
#define DELETECNT(PTR, TYPE) qp::Memory::GetInstance().Delete(PTR, __FILE__, __LINE__, TYPE);
#else
#define NEWCNT(PTR, TYPE)
#define DELETECNT(PTR, TYPE)
#endif

#ifdef _DEBUG
#define qpNewPtr(P, T) P = ::new(std::nothrow) T; if (P == 0) qpDbgError(); else NEWCNT(P, 1);
#define qpNewPtrEx(P, T) T* P = ::new(std::nothrow) T; if (P == 0) qpDbgError(); else NEWCNT(P, 1);
#define qpNewArray(P, T, S) P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError(); else NEWCNT(P, 2);
#define qpNewArrayEx(P, T, S) T* P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError(); else NEWCNT(P, 2);
#define qpMalloc(P, T, S) P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError(); else NEWCNT(P, 3);
#define qpMallocEx(P, T, S) T* P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError(); else NEWCNT(P, 3);
#define qpMallocNew(P, S) void* P = ::malloc(S); if (P == 0) qpDbgError(); else NEWCNT(P, 4);
#define qpDelPtr(P) if (P != 0) { DELETECNT(P, 1); ::delete P; P = 0; }
#define qpDelArray(P) if (P != 0) { DELETECNT(P, 2); ::delete[] P; P = 0; }
#define qpFree(P) if (P != 0) { DELETECNT(P, 3); ::free(P); P = 0; }
#define qpFreeDelete(P) if (P != 0) { DELETECNT(P, 4); ::free(P); P = 0; }
#else
#define qpNewPtr(P, T) P = ::new(std::nothrow) T; if (P == 0) qpDbgError();
#define qpNewPtrEx(P, T) T* P = ::new(std::nothrow) T; if (P == 0) qpDbgError();
#define qpNewArray(P, T, S) P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError();
#define qpNewArrayEx(P, T, S) T* P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError();
#define qpMalloc(P, T, S) P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError();
#define qpMallocEx(P, T, S) T* P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError();
#define qpMallocNew(P, S) void* P = ::malloc(S); if (P == 0) qpDbgError();
#define qpDelPtr(P) if (P != 0) { ::delete P; P = 0; }
#define qpDelArray(P) if (P != 0) { ::delete[] P; P = 0; }
#define qpFree(P) if (P != 0) { ::free(P); P = 0; }
#define qpFreeDelete(P) if (P != 0) { ::free(P); P = 0; }
#endif

namespace qp
{

#ifdef _DEBUG

class Memory
{
struct MemoryData
{
MemoryData(const char* file, int line, int type) : file(file), line(line), type(type) {}
void* ptr;
std::string file;
int line;
int type;
};
public:
~Memory() { Analyze(); }
static Memory& GetInstance();
void New(void* ptr, const char* file, int line, int type);
void Delete(void* ptr, const char* file, int line, int type);

private:
void Analyze();

private:
typedef std::map<void*, MemoryData> MemoryMap;
MemoryMap m_memMap;
};

#endif

}


实现:
/*
* Copyright (C) QPSOFT.COM All rights reserved.
*/

#include <qp/Inc.h>
#include <qp/Qp.h>
#include <qp/Memory.h>

#include <qp/File.h>

using namespace qp;

#ifdef _DEBUG

Memory& Memory::GetInstance()
{
static Memory mem;
return mem;
}

void Memory::New(void* ptr, const char* file, int line, int type)
{
if (m_memMap.find(ptr) != m_memMap.end())
{
Debug::Printf("Memory Leak? %s, %d, %d", file, line, type);
qpASSERT(m_memMap.find(ptr) == m_memMap.end());
}
else
{
m_memMap.insert(std::make_pair(ptr, MemoryData(file, line, type)));
}
}

void Memory::Delete(void* ptr, const char* file, int line, int type)
{
MemoryMap::iterator it = m_memMap.find(ptr);
if (it == m_memMap.end())
{
Debug::Printf("Memory Leak? %s, %d, %d", file, line, type);
qpASSERT(it != m_memMap.end());
}
else
{
qpASSERT(it->second.type == type);
if (it->second.type == type)
{
m_memMap.erase(it);
}
}
}

void Memory::Analyze()
{
if (!m_memMap.empty())
{
File file;
file.Create(_T("MemoryReport.log"), File::CreateAlways);
int cnt = 0;
for (MemoryMap::iterator it = m_memMap.begin(); it != m_memMap.end(); ++it)
{
std::string buf(Global::Format("%d: %s, %d, %d\r\n", ++cnt, it->second.file.c_str(),
it->second.line, it->second.type));
file.Write(buf.c_str(), buf.size());
}
}
else
{
::DeleteFile(_T("MemoryReport.log"));
}
}

#endif
yshuise 2009-12-01
  • 打赏
  • 举报
回复
#define qpDelPtr(P) if (P != NULL) { ::delete P; P = NULL; }
#define qpDelArray(P) if (P != NULL) { ::delete[] P; P = NULL; }
==============
delete对于NULL可以自己处理。
我是苦力 2009-12-01
  • 打赏
  • 举报
回复
To loaden:
我这里有个个案,暂时抛开你的两个问题,来看看能不能工作。
有个程序,由exe,一个Main DLL, 7、8个子DLL,和一Lib组成。
exe 为框架
MainDLL 负责其他DLL的管理,插件形式
Lib 主要是工程的数据结构部分,各种数据对象在这里定义和实现。
除了exe,那些DLL Link 的时候都要用到LIb。Lib里面的对象都在Lib里面产生。

我想请问在Main dll 入口中加MemoryLeakChecker,能check到整个程序的内存情况吗?框架的除外。 好像内存泄露的检测如果都在一个模块中,检测就还好点,可是如果涉及到N个模块的话就复杂多了
lklwlklw 2009-12-01
  • 打赏
  • 举报
回复
new重载
taodm 2009-12-01
  • 打赏
  • 举报
回复
换个好点的商用内存泄漏检测工具。
gjy1606 2009-12-01
  • 打赏
  • 举报
回复
高手贴,MARK!
老邓 2009-12-01
  • 打赏
  • 举报
回复
[Quote=引用 29 楼 taodm 的回复:]
基于简单的_CrtSetReportMode函数族来实现内存检测本来就是不靠谱的。
[/Quote]
内存泄露,只要编代码的时候多加小心,_CrtSetReportMode辅助,还是够用的。
顶上去,看有无最佳解决方案。

请大家点评一下 #27 楼的缺陷,以及应该改进的地方。
老邓 2009-11-30
  • 打赏
  • 举报
回复
[Quote=引用 21 楼 mstlq 的回复:]
饮酒过度,今晚也没法思考,只能飘飘然看点偶像剧了……
不过,我还真的没搞清楚楼主关于检查内存泄露类的需求……
[/Quote]睡不着啊,起来写了个头文件:
#pragma once

#define qpNew(PTR, TYPE) qp::Memory::GetInstance().New(PTR, __FILE__, __LINE__, TYPE)
#define qpDelete(PTR, TYPE) qp::Memory::GetInstance().Delete(PTR, TYPE);

#ifdef _DEBUG
#define qpNewPtr(P, T) P = ::new(std::nothrow) T; if (P == 0) qpDbgError(); else qpNew(P, 1);
#define qpNewPtrEx(P, T) T* P = ::new(std::nothrow) T; if (P == 0) qpDbgError(); else qpNew(P, 1);
#define qpNewArray(P, T, S) P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError(); else qpNew(P, 2);
#define qpNewArrayEx(P, T, S) T* P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError(); else qpNew(P, 2);
#define qpMalloc(P, T, S) P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError(); else qpNew(P, 3);
#define qpMallocEx(P, T, S) T* P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError(); else qpNew(P, 3);
#define qpDelPtr(P) if (P != 0) { qpDelete(P, 1); ::delete P; P = 0; }
#define qpDelArray(P) if (P != 0) { qpDelete(P, 2); ::delete[] P; P = 0; }
#define qpFree(P) if (P != 0) { qpDelete(P, 3); ::free(P); P = 0; }
#else
#define qpNewPtr(P, T) P = ::new(std::nothrow) T; if (P == 0) qpDbgError();
#define qpNewPtrEx(P, T) T* P = ::new(std::nothrow) T; if (P == 0) qpDbgError();
#define qpNewArray(P, T, S) P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError();
#define qpNewArrayEx(P, T, S) T* P = ::new(std::nothrow) T[S]; if (P == 0) qpDbgError();
#define qpMalloc(P, T, S) P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError();
#define qpMallocEx(P, T, S) T* P = (T*)::malloc((S) * sizeof(T)); if (P == 0) qpDbgError();
#define qpDelPtr(P) if (P != 0) { ::delete P; P = 0; }
#define qpDelArray(P) if (P != 0) { ::delete[] P; P = 0; }
#define qpFree(P) if (P != 0) { ::free(P); P = 0; }
#endif

namespace qp
{

class Memory
{
struct MemoryData
{
String& file;
int line;
int type;
};
public:
static Memory& GetInstance();
void New(void* ptr, const String& file, int line, int type);
void Delete(void* ptr, int type);

private:
typedef std::map<void*, MemoryData> MemoryMap;
MemoryMap m_memMap;
};

}
加载更多回复(21)

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧