很早之前,我第一次接触BREW的时候,他们告诉我说只能用C,那个时候,BREW好像刚出到SDK 2.1,也说不定没出来,后来,我用C的struct来模拟C++的类及Java类。第一次的接触大概持续了不到两个月,第二次的接触却在两年之后,而这次,接触的也大部分是C的代码,通过查看编译器的资料,知道可以对C++进行大部分支持了,而这一次,却持续了3年之久,并且可能还会持续一段不小的时间。
话题扯远了,第二次的时候,看了下别人的C++代码,每个类里面都要重载new,delete,对于类似我这样的懒人实在是不方便,于是就写了个重载全局new,delete等函数的代码,如下所示:
.h
extern void* operator new( size_t sz );
extern void operator delete( void* mi );
extern void* operator new[]( size_t sz );
extern void operator delete[]( void* mi );
.cpp
/*!
* @brief 重载new操作符
*
* @param sz 欲分配的内存大小
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new操作符
*/
void* operator new ( size_t sz )
{
if( sz == 0 )
return NULL;
void* mi = MALLOC( sz );
return mi;
}
/*!
* @brief 重载delete操作符
*
* @param mi 欲释放的内存地址
* @return 无
* @remark
*
* 在ARM中如果使用C++,必须重载delete函数
*/
void operator delete( void* mi )
{
if( mi == NULL )
return;
FREE( mi );
mi = NULL;
}
/*!
* @brief 重载new[]操作数
*
* @param sz 欲分配内存的大小
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new[]函数
*/
void* operator new[]( size_t sz )
{
if( sz == 0 )
return NULL;
void* mi = MALLOC( sz );
return mi;
}
/*!
* @brief 重载delete[]函数
*
* @param mi 欲释放的内存地址
* @return 无
* @remark
*
* 在ARM中如果使用C++,必须重载delete[]函数
*/
void operator delete[]( void* mi )
{
if( mi == NULL )
return;
FREE( mi );
mi = NULL;
}
上面的代码在使用不久,大约不到一周的样子,被我更改了,因为我需要一个可以检测内存泄漏的玩意,于是,就将之前提到的检测内存泄漏的C++版本的代码加入了,大约用了大半年的样子,对BREW程序的测试流程已非常熟悉了,三思之后,制作了内存管理器。在此之后的代码维持了很长时间,直到现在还在使用。
.h文件变成了这个样子:
/*!
* @file MemoryAlloc.h
* @brief 内存分配
*
* 此文件为内存分配并提供简单的内存检测工具。内存检测工具为可以选择的,如果要使用内存检测,则添加#define _DEBUG1。添加#define TEST_MEM可以用来测试程序使用的内存量。如果使用预分配内存管理,则添加#define USE_MEMMANAGER。
* 需要注意的是,如果使用new Type或者new Type[]的方式,则不会使用预分配内存管理。必须使用new (MEM_TYPE) Type或者new (MEM_TYPE)的方式进行分配。删除内存则不需要进行特殊处理。在程序的AEEApplet之后,必须放置CMemoryManger实例。具体用法可参考例子test_MemoryAlloc
* @copyright GPL
* @warning
*
* 内存检测仅仅可以在wis平台上使用,ARM平台不可以使用
*/
#ifndef _H_MEMORYALLOC
#define _H_MEMORYALLOC
#include "AEEStdlib.h"
extern void* operator new( size_t sz );
extern void operator delete( void* mi );
extern void* operator new[]( size_t sz );
extern void operator delete[]( void* mi );
extern void* operator new( size_t sz, int atype );
extern void* operator new[]( size_t sz, int atype );
#endif
.cpp文件变成了这样:
#include "MemoryAlloc.h"
#ifdef TEST_MEM
#include "AEEHeap.h"
#include "AEEAppGen.h"
#endif
#ifdef USE_MEMMANGER
#include "MemoryManger.h"
#include "AEEAppGen.h"
#endif
#ifdef _DEBUG1
#include "MemoryLog.h"
MEMORYLOG iMemLog;//!< 全局变量,用于记录指针,在正常情况下仅存这一个变量
#endif
#ifdef TEST_MEM
static int memuse = 0; //!< 程序使用的内存量
static int used = 0; //!< 已经使用的内存量
/*!
* @class Log
* @brief 空类,用来在程序结束之后打印出程序使用的内存量
*
* @waring
*
* 内存检测仅仅可以在wins平台使用,arm平台不可以使用
* @copyright GPL
*/
class Log{
public:
/*!
* @brief 构造函数
*/
Log()
{
}
/*!
* @brief 析构函数
*/
~Log()
{
DBGPRINTF("Mem:%d", used );
}
};
Log MyLog; //!< 用来在程序结束之后打印程序使用内存量的变量
#endif
/*!
* @brief 重载new操作符
*
* @param sz 欲分配的内存大小
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new操作符
*/
void* operator new ( size_t sz )
{
if( sz == 0 )
return NULL;
void* mi = MALLOC( sz );
#ifdef _DEBUG1
if( !mi )
{
DBGPRINTF("out memory");
}
else
{
MEMSTRUCT* toadd = (MEMSTRUCT*)MALLOC( sizeof( MEMSTRUCT ) );
toadd->addr = mi;
toadd->length = sz;
iMemLog.Add(toadd);
}
#endif
#ifdef TEST_MEM
IHeap* pHeap= NULL;
AEEApplet* pMe = (AEEApplet*)GETAPPINSTANCE();
ISHELL_CreateInstance( pMe->m_pIShell, AEECLSID_HEAP, ( void** )( & pHeap ) );
if( memuse == 0 )
{
used = memuse = IHEAP_GetMemStats( pHeap );
}
if( IHEAP_GetMemStats( pHeap ) - memuse > used )
{
used = IHEAP_GetMemStats( pHeap ) - memuse;
}
IHEAP_Release( pHeap );
#endif
return mi;
}
/*!
* @brief 重载delete操作符
*
* @param mi 欲释放的内存地址
* @return 无
* @remark
*
* 在ARM中如果使用C++,必须重载delete函数
*/
void operator delete( void* mi )
{
if( mi == NULL )
return;
#ifdef USE_MEMMANGER
CMemoryManger* manger = ( CMemoryManger* )( ( byte* )( GETAPPINSTANCE() ) + sizeof( AEEApplet ) );
if( manger->MemoryFree( mi ) )
return;
#endif
#ifdef _DEBUG1
iMemLog.Sub(mi);
#endif
FREE( mi );
mi = NULL;
}
/*!
* @brief 重载new[]操作数
*
* @param sz 欲分配内存的大小
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new[]函数
*/
void* operator new[]( size_t sz )
{
if( sz == 0 )
return NULL;
void* mi = MALLOC( sz );
#ifdef _DEBUG1
if( !mi )
{
DBGPRINTF("our memory");
}
else
{
MEMSTRUCT* toadd = (MEMSTRUCT*)MALLOC( sizeof( MEMSTRUCT ) );
toadd->addr = mi;
toadd->length = sz;
iMemLog.Add(toadd);
}
#endif
#ifdef TEST_MEM
IHeap* pHeap= NULL;
AEEApplet* pMe = (AEEApplet*)GETAPPINSTANCE();
ISHELL_CreateInstance( pMe->m_pIShell, AEECLSID_HEAP, ( void** )( & pHeap ) );
if( memuse == 0 )
{
used = memuse = IHEAP_GetMemStats( pHeap );
}
if( IHEAP_GetMemStats( pHeap ) - memuse > used )
{
used = IHEAP_GetMemStats( pHeap ) - memuse;
}
IHEAP_Release( pHeap );
#endif
return mi;
}
/*!
* @brief 重载delete[]函数
*
* @param mi 欲释放的内存地址
* @return 无
* @remark
*
* 在ARM中如果使用C++,必须重载delete[]函数
*/
void operator delete[]( void* mi )
{
if( mi == NULL )
return;
#ifdef USE_MEMMANGER
CMemoryManger* manger = ( CMemoryManger* )( ( byte* )( GETAPPINSTANCE() ) + sizeof( AEEApplet ) );
if( manger->MemoryFree( mi ) )
return;
#endif
#ifdef _DEBUG1
iMemLog.Sub(mi);
#endif
FREE( mi );
mi = NULL;
}
/*!
* @brief 重载new操作符
*
* @param sz 欲分配的内存大小
* @param atype 欲分配的内存的种类
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new操作符
*/
void* operator new( size_t sz, int atype )
{
if( sz == 0 )
return NULL;
#ifdef USE_MEMMANGER
CMemoryManger* manger = ( CMemoryManger* )( ( byte* )( GETAPPINSTANCE() ) + sizeof( AEEApplet ) );
void* mi = manger->MemoryAlloc( atype, sz );
#else
void* mi = MALLOC( sz );
#endif
#ifdef _DEBUG1
if( !mi )
{
DBGPRINTF("out memory");
}
else
{
MEMSTRUCT* toadd = (MEMSTRUCT*)MALLOC( sizeof( MEMSTRUCT ) );
toadd->addr = mi;
toadd->length = sz;
iMemLog.Add(toadd);
}
#endif
#ifdef TEST_MEM
IHeap* pHeap= NULL;
AEEApplet* pMe = (AEEApplet*)GETAPPINSTANCE();
ISHELL_CreateInstance( pMe->m_pIShell, AEECLSID_HEAP, ( void** )( & pHeap ) );
if( memuse == 0 )
{
used = memuse = IHEAP_GetMemStats( pHeap );
}
if( IHEAP_GetMemStats( pHeap ) - memuse > used )
{
used = IHEAP_GetMemStats( pHeap ) - memuse;
}
IHEAP_Release( pHeap );
#endif
return mi;
}
/*!
* @brief 重载new[]操作符
*
* @param sz 欲分配的内存大小
* @param atype 欲分配的内存的种类
* @return 已经分配的内存地址
* @remark
*
* 在ARM中如果使用C++,必须重载new操作符
*/
void* operator new[]( size_t sz, int atype )
{
if( sz == 0 )
return NULL;
#ifdef USE_MEMMANGER
void* test = (void*)( GETAPPINSTANCE() );
CMemoryManger* manger = ( CMemoryManger* )( ( byte* )( GETAPPINSTANCE() ) + sizeof( AEEApplet ) );
void* mi = manger->MemoryAlloc( atype, sz );
#else
void* mi = MALLOC( sz );
#endif
#ifdef _DEBUG1
if( !mi )
{
DBGPRINTF("out memory");
}
else
{
MEMSTRUCT* toadd = (MEMSTRUCT*)MALLOC( sizeof( MEMSTRUCT ) );
toadd->addr = mi;
toadd->length = sz;
iMemLog.Add(toadd);
}
#endif
#ifdef TEST_MEM
IHeap* pHeap= NULL;
AEEApplet* pMe = (AEEApplet*)GETAPPINSTANCE();
ISHELL_CreateInstance( pMe->m_pIShell, AEECLSID_HEAP, ( void** )( & pHeap ) );
if( memuse == 0 )
{
used = memuse = IHEAP_GetMemStats( pHeap );
}
if( IHEAP_GetMemStats( pHeap ) - memuse > used )
{
used = IHEAP_GetMemStats( pHeap ) - memuse;
}
IHEAP_Release( pHeap );
#endif
return mi;
}
对new和new[]分别添加了参数,并且,并不推荐程序调用默认的new和new[],而是通过类似下面的方式进行调用:
#define ELEAVE 0
byte* ptr = new (ELEAVE) byte[10];
熟悉symbian的人应该对此很熟悉,delete和普通的没有区别。推荐的原因在于如果使用了内存管理器需要放在AEEApplet之后,类似下面这样:
class test_MemoryAlloc
{
public:
AEEApplet a; //!< First element of this structure must be AEEApplet
CMemoryManger m_manger; //!< 内存管理器,必须放在AEEApplet的后面,由于AEEApplet是第一个元素,也就是说,内存管理器将是第二个元素。
AEEDeviceInfo DeviceInfo; //!< always have access to the hardware device information
};
上述代码在查找内存检测的代码如下:
#ifndef _H_MEMORYLOG
#define _H_MEMORYLOG
/*!
* @struct MEMSTRUCT
* @brief 内存检测用来记录内存的结构
*
* @copyright GPL
*/
typedef struct _MEMSTRUCT{
void* addr;//!< 分配的内存地址
int length;//!< 分配的内存征长度
int count;//!< 分配时的分配索引值
} MEMSTRUCT;
class CMemoryManger;
/*!
* @class MEMORYLOG
* @brief 内存记录
*
* @warning
*
* 内存检测仅仅可以在wins平台使用,ARM平台不可以使用
* @copyright GPL
*/
class MEMORYLOG
{
private:
friend class CMemoryManger;
MEMSTRUCT** imem; //!< 已经记录的内存结构
int num;//!< 目前已经记录的内存记录数目
int maxnew;//!< 总共分配的内存次数
int maxdelete;//!< 总共删除的内存次数
public:
/*!
* @fn MEMORYLOG
* @brief 构造函数
*
*/
MEMORYLOG(): imem( NULL ), num( 0 ), maxnew( 0 ), maxdelete( 0 )
{
}
/*!
* @fn Add( MEMSTRUCT* amem )
* @brief 添加一个记录结构
*
* @param amem 欲添加的记录结构指针
* @return 无
*/
void Add( MEMSTRUCT* amem )
{
MEMSTRUCT** temp = (MEMSTRUCT**)MALLOC( sizeof( MEMSTRUCT* ) * ( num + 1 ) );
for( int i = 0;i < num; i++ )
temp[i] = imem[i];
temp[num] = amem;
FREE( imem );
imem = temp;
if( maxnew == 6 )
int k = 10;
imem[num]->count = maxnew;
num++;
maxnew++;
}
/*!
* @fn SubNode( int aindex )
* @brief 从内存记录中删除一个记录
*
* @param aindex 欲删除的记录索引
* @return 无
*/
void SubNode( int aindex )
{
MEMSTRUCT** temp = (MEMSTRUCT**)MALLOC( sizeof( MEMSTRUCT* ) * ( num - 1 ) );
for( int i = 0;i < num; i++ )
if( i != aindex )
temp[i] = imem[i];
else
break;
FREE( imem[i] );
for( i = i+1; i < num; i++ )
temp[i-1] = imem[i];
FREE( imem );
imem = temp;
num--;
maxdelete--;
}
/*!
* @fn Sub( void* aaddr )
* @brief 从内存记录中删除一个记录
*
* @param aaddr 分配了的内存地址
* @return 无
*/
void Sub( void* aaddr )
{
int i = 0;
for( i = 0; i < num; i++ )
{
if( imem[i]->addr == aaddr )
break;
}
if( i == num )
{
return;
}
SubNode( i );
}
/*!
* @fn ~MEMORYLOG()
* @brief 析构函数
*
* @return 无
*/
~MEMORYLOG()
{
if( num != 0 )
{
if( num > 0 )
{
for( int i = 0;i < num; i++ )
{
char temp[255];
SNPRINTF(temp, sizeof(temp), "memory leak addr = %x, size = %d, count = %d", imem[i]->addr, imem[i]->length, imem[i]->count );
DBGPRINTF( temp );
}
}
}
char temp[255];
SNPRINTF(temp, sizeof(temp), "memory resulet new count = %d, delete count = %d", maxnew, maxdelete );
DBGPRINTF( temp );
for( int i = 0; i < num; i++ )
FREE(imem[i]);
FREE( imem );
}
};
#endif
上面的代码给我自己带来的最大的感受就是,我的耳根清静了不少,我告诉别人上面的如何使用,剩下的就是他们的事情了,这样比别人总来找我:“不好意思,这个程序不知道哪里出了内存泄漏,查找要很长时间”要好的多。