上一章那东西怎么用?很简单。比如:
SmartHeapMemPtr<int> p = NEW int[100];
for (int i = 0; i <= 100; i ++)
p[i] = 0; // 前100次正常,到最后一个循环时抛出异常
你可以给SmartHeapMemPtr
类里的那4个指针内容访问函数里的抛出异常的语句打上断点。这样一旦走到断点,就表明有越界了,此时查看call stack,就可以找到代码问题所在。
越界抛出异常是怎么办到的?SmartHeapMemPtr<int> p = NEW int[100];
这个语句在执行时,会调用SmartHeapMemPtr::operatore=
函数,就是下面这个
SmartHeapMemPtr& operator=(const T* p)
{
...
m_pData->m_pEndMemPtr = m_pData->m_pStartMemPtr + pPrefix->d.pPostfix->nActualSize - 1;
...
}
这样分配给p
的内存块的范围它就获得了。接下来每次循环访问p[i]
时,会调用inline T& operator [](const int n)
,里面做了个当前指针访问地址是否超出了范围的判断。
那么如果我bug都检查完了,该发布release版的时候,该怎么关掉越界检查?我们下面定义一个宏
#ifdef USE_SMART_HEAP
#define HEAP_MEM_PTR(a) VFC::SmartHeapMemPtr<a>
#else
#define HEAP_MEM_PTR(a) a*
#endif
然后最上面的那个测试程序的变量申明和分配就可以改成
HEAP_MEM_PTR(int) p = NEW int[100];
定义了USE_SMART_HEAP
时,p
就是个智能指针,未定义时就是个普通指针。我们再结合内存池,做以下宏定义
#if defined USE_SMART_HEAP
#ifndef USE_MEM_POOL
#define USE_MEM_POOL
#endif
#define HEAP_MEM_PTR(a) VFC::SmartHeapMemPtr<a>
#else
#define HEAP_MEM_PTR(a) a*
#endif
#if defined USE_MEM_POOL
#define NEW new(__LINE__, FILE_NAME)
#define DEL_PTR(p) { DestructPtr(p); }
#define DEL_ARRAY(p) { DestructArray(p); }
#else
#define NEW new
#define DEL_PTR(p) delete p;
#define DEL_ARRAY(p) delete[] p;
#endif
所有指针或数组都采用NEW
、DEL_PTR
、和DEL_ARRAY
进行分配和释放操作。这里提供了三种选择。当既不做泄露和越界检查(不定义USE_SMART_HEAP
)也不用内存池(也不定义USE_MEM_POOL
)时,这三个宏分别对应C++内置的分配释放操作。第二种选择是采用内存池但不做泄露和越界检查(定义USE_MEM_POOL
但不定义USE_SMART_HEAP
)。第三种选择是既采用内存池也做泄露和越界检查(定义USE_SMART_HEAP
)。这里注意,做泄露和越界检查必须采用内存池,因为越界检查里的内存块范围需要通过MemPool里定义的内存块的前缀来获得(至于泄露检查就不用说了)。
哦,还漏了DEL_PTR(p)
、和DEL_ARRAY(p)
对于p
是SmartHeapMemPtr
的情况。加上
template<typename T> inline void DestructPtr(VFC::SmartHeapMemPtr<T>& p) {
p.DecRef(); p.Release();
}
template<typename T> inline void DestructArray(VFC::SmartHeapMemPtr<T>& p) {
p.DecRef(); p.Release();
}
好了,我们重新写一下本章开头的测试程序。应该这样写
HEAP_MEM_PTR<int> p = NEW int[100];
for (int i = 0; i <= 100; i ++)
p[i] = 0; // 前100次正常,到最后一个循环时抛出异常
DEL_ARRAY(p);
只要程序里所有的指针相关内容都这么写,就可以做到泄露和越界的自动检查了。当然,要开了USE_SMART_HEAP
宏。
可是,事情没那么简单,下章再说。