一个C++的内存池和内存管理的实现(五)

前面我们实现的这个内存池使用起来,可能会遇到不少问题。比如,某同学申请内存的时候,用的是

whatever *p = new whatever;

或者其他的,而不是

whatever *p = new(__LINE__, FILE_NAME) whatever;

结果这块内存仍旧走的是系统,但是释放的时候却调用的内存池的释放函数,这就是崩的节奏啊。还有,像微软MFC里的AfxMem.cpp和AfxMem.h里也有new和delete的placement,会不会冲突?打开看看,有一大堆。还好还好,我们的new还是有区别的,行号在文件名的前面,是第二个参数。微软的是反过来的。

太不保险了吧!别人如果也overload了相同参数的new,编译都会通不过。而且你又怎么能保证别人的new会走到你的内存池里?还是别霸占delete了,给人给己留条活路吧!

所以我们还是得改一下,不要overload delete函数。让我们定义三个宏

#define NEW new(__LINE__, FILE_NAME)
#define DEL_PTR(p) { DestructPtr(p); }
#define DEL_ARRAY(p) { DestructArray(p); }

NEW用来申请内存(包括数组),DEL_PTR用来删除指针,DEL_ARRAY用来删除数组。在程序申请或释放内存时,就调用这三个宏,避免和其他代码的new、delete混杂不清。这里NEW还是调用了我们的operator new函数(这样就我们就不用管那些构造函数的调用了)。两个删除的宏里出现了新的函数,我们看一下

template<typename T> inline void DestructPtr(T* p) {
    p->~T();  // 调用析构函数
    MemPool::GetInstance().Free(p);
}

template<typename T> inline void DestructArray(T* p) {
    if (((DWORD_PTR) p) % 16 != 0) {  // 没有16字节对齐,说明这是一个有析构的类的数组
        DWORD_PTR p0 = ((DWORD_PTR) p) & ~(16 - 1);  // 找到正确的分配到的内存位置
        int nElementCnt = *((int*) p0), i = 0;  // 数组长度
        for (T* pCur = p; i < nElementCnt; i ++, pCur ++)
            pCur->~T();  // 为数组里每个元素调用析构

        VFC::MemPool::GetInstance().Free((void*) p0);
    } else
        VFC::MemPool::GetInstance().Free((void*) p);
}

还记得最开始提到过的有析构的类的数组的申请么?申请到的内存块其实多给了4个字节,并且数组指针从第4个字节开始,前面的就是数组长度。所以我们一旦在释放时发现传进来的指针数组不是16字节对齐的,我们就找到前面的那个对齐的地址,那里存放着数组元素个数。

接下来的几章,我们将加入泄露的查找功能

你可能感兴趣的:(编程)