我们的这款游戏从开发到上线至今已有大概4年了,昨天发现代码中有new出来的对象没有delete,程序退出后VS输出栏中居然没有提示,难道程序中没有内存泄漏检察?进一步的求证确定了我的担忧(我是半路进的项目组)。在代码中加上_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF)后,VS的输出栏赫然显示了大量的内存泄漏。在动手解决内存泄漏之前,先复习一下有关new操作符重载的相关知识:
new 操作符按其可见性可分为全局的与局部的(类成员),我们可以对他们进行重载,下面是VS 2010中new.cpp中new操作符的声明
1 void * operator new( size_t size ); //1
2 void * operator new ( size_t size, int nBlockUse, const char * szFileName, int nLine ); //2
void main()
{
char * p = new char; //调用第1个new
p = new ( _NORMAL_BLOCK, __FILE__, __LINE__ ) char; //调用第2个new
}
调用new时,传入的参数比new声明时的参数少了一个,即第一个参数,该参数是new的对象的大小,由编译器填写,禁止程序员自己插手。
对一个类重载new和delete
class Student
{
void * operator new( size_t size ) //3
{
std::cout << __FUNCTION__ << std::endl;
return malloc( sizeof(Student) );
}
};
Student * pS = new Student(); //调用Student类内部的new,即第3个new
当我们new一个自定义类时,如果该类重载了new操作符,则优先使用该类内部定义的new,如果这时非要调用全局的new操作符怎么办呢?可以在new前面加两个冒号:
pS = ::new Student(); //调用全局的new,第1个new
下面回到解决内存泄漏的步骤上:
1 在程序必定会执行的路径上加上:
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
不一定非得是程序的最开始处
2 使用上面给出的第2个new操作符,当程序退出时,它会在VS输出栏中注明是哪行代码引起的内存泄漏。即所有的new 都改为
new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
完了。。。。。这样就可以了。
但是,我们程序已有的代码都写的是new,而不是new ( _NORMAL_BLOCK, __FILE__, __LINE__ )。
MFC中的解决方法:
#define new new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
这样好像是可以解决问题,但是我们游戏中用了Gamebryo游戏引擎,这个引擎中很多类内部重载了new操作符,并且还有如下的宏定义:
#define NiNew new( NI_MEMHINT_NONE, __FILE__, __LINE__ , __FUNCTION__ )
并且引擎自带的对象都是通过NiNew来创建的,如 NiNew NiAlphaProperty();
如果我们把new 宏定义成了new ( _NORMAL_BLOCK, __FILE__, __LINE__ ),那么碰到NiNew NiAlphaProperty()后,代码就被展开成为
new ( _NORMAL_BLOCK, __FILE__, __LINE__ ) ( NI_MEMHINT_NONE, __FILE__, __LINE__ , __FUNCTION__ ) NiAlphaProperty(); 当编译器尝试着用此代码与NiAlphaProperty类内部定义的new操作符去匹配时就抓瞎了,只能报错。
目前就只能自己再定义一个宏:
#define New ::new ( _NORMAL_BLOCK, __FILE__, __LINE__ )
将代码中所有的new替换成New。
另一种内存泄漏检测方法就是借用第3方的库:
Visual Leak Detector 是一个不错的泄漏检测库,下载地址:
http://vld.codeplex.com/releases/view/82311
只需要在自己的程序的main函数所在的文件中#include <vld.h>就可以了,使用起来很方便。
(注: _CrtSetBreakAlloc()对大型的C++工程不太实用 )