7 对new与delete操作符的重载 new与delete内存空间动态分配操作符是C++中使用堆进行内存管理的一种常用方式,在程序运行过程中可以根据需要随时通过这两个操作符建立或删除堆对象。new操作符将在堆中分配一个足够大小的内存块以存放指定类型的对象,如果每次构造的对象类型不同,则需要按最大对象所占用的空间来进行分配。new操作符在成功执行后将返回一个类型与new所分配对象相匹配的指针,如果不匹配则要对其进行强制类型转换,否则将会编译出错。在不再需要这个对象的时候,必须显式调用delete操作符来释放此空间。这一点是非常重要的,如果在预分配的缓冲里构造另一个对象之前或者在释放缓冲之前没有显式调用delete操作符,那么程序将产生不可预料的后果。在使用delete操作符时,应注意以下几点:
1) 它必须使用于由运算符new返回的指针
2) 该操作符也适用于NULL指针
3) 指针名前只用一对方括号符,并且不管所删除数组的维数,忽略方括号内的任何数字
class CVMShow{ private: static HANDLE m_sHeap; static int m_sAllocedInHeap; public: LPVOID operator new(size_t size); void operator delete(LPVOID pVoid); }
…… HANDLE m_sHeap = NULL; int m_sAllocedInHeap = 0; LPVOID CVMShow::operator new(size_t size) { if (m_sHeap == NULL) m_sHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0); LPVOID pVoid = HeapAlloc(m_sHeap, 0, size); if (pVoid == NULL) return NULL; m_sAllocedInHeap++; return pVoid; } void CVMShow::operator delete(LPVOID pVoid) { if (HeapFree(m_sHeap, 0, pVoid)) m_sAllocedInHeap--; if (m_sAllocedInHeap == 0) { if (HeapDestory(m_sHeap)) m_sHeap = NULL; } }
|
在程序中除了直接用上述方法使用new和delete来建立和删除堆对象外,还可以通过为C++类重载new和delete操作符来方便地利用堆栈函数。上面的代码对它们进行了简单的重载,并通过静态变量m_sHeap和m_sAllocedInHeap在类CVMShow的所有实例间共享唯一的堆句柄(因为在这里CVMShow类的所有实例都是在同一个堆中进行内存分配的)和已分配类对象的计数。这两个静态变量在代码开始执行时被分别初始化为NULL指针和0计数。
重载的new操作符在第一次被调用时,由于静态变量m_sHeap为NULL标志着堆尚未创建,就通过HeapCreate()函数创建一个堆并返回堆句柄到m_sHeap。随后根据入口参数size所指定的大小在堆中分配内存,同时已分配内存块计数器m_sAllocedInHeap累加。在该操作符的以后调用过程中,由于堆已经创建,故不再创建堆,而是直接在堆中分配指定大小的内存块并对已分配的内存块个数进行计数。
在CVMShow类对象不再被应用程序所使用时,需要将其撤消,由重载的delete操作符完成此工作。delete操作符只接受一个LPVOID型参数,即被删除对象的地址。该函数在执行时首先调用HeapFree()函数将指定的已分配内存的对象释放并对已分配内存计数递减1。如果该计数不为零则表明当前堆中的内存块没有全部释放,堆暂时不予撤消。如果m_sAllocedInHeap计数减到0,则堆中已释放完所有的CVMShow对象,可以调用HeapDestory()函数将堆销毁,并将堆句柄m_sHeap设置为NULL指针。这里在撤消堆后将堆句柄设置为NULL指针的操作是完全必要的。如果不执行该操作,当程序再次调用new操作符去分配一个CVMShow类对象时将会认为堆是存在的而会试图在已撤消的堆中去分配内存,显然将会导致失败。
象CVMShow这样设计的类通过对new和delete操作符的重载,并且在一个堆中为所有的CVMShow类对象进行分配,可以节省在为每一个类都创建堆的分配开销与内存。这样的处理还可以让每一个类都拥有属于自己的堆,并且允许派生类对其共享,这在程序设计中也是比较好的一种处理方法。
8 小结 在使用堆时有时会造成系统运行速度的减慢,通常是由以下原因造成的:分配操作造成的速度减慢;释放操作造成的速度减慢;堆竞争造成的速度减慢;堆破坏造成的速度减慢;频繁的分配和重分配造成的速度减慢等。其中,竞争是在分配和释放操作中导致速度减慢的问题。基于上述原因,建议不要在程序中过于频繁的使用堆。文中所述代码均在Windows 2000 Professional下由Microsoft Visual C++ 6.0编译通过。