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

本文深入探讨了内存池技术在C++中的应用,通过单模类实现内存申请与释放,并介绍了如何管理静态类中从内存池申请的内存,避免内存泄漏和越界错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

书接上回,我们做了个单模类,里面提供两个重要的函数Alloc和Free供内存申请和释放时调用。接着我们可以overload operator new和delete函数,在里面分别调用Alloc和Free

void *operator new (size_t nSize, int nLine, const char* szFile)
{
    return MemPool::GetInstance().Alloc((int) nSize, szFile, nLine, false);
}

void operator delete(void *p)
{
    MemPool::GetInstance().Free(p);
}

new函数可以各种overload,具体可以google一下。这里的overload,除了第一个size_t的尺寸参数外,后面两个参数对应你源文件里的行数和文件名。这个是用来定位内存泄露的,后面会讲。用的时候,申请代码的那行这么写

whatever *p = new(__LINE__, FILE_NAME) whatever

其中FILE_NAME是个宏,表示当前的源文件名(去掉了目录的)

#define FILE_NAME ((strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') : __FILE__ - 1) + 1)

上面的__LINE____FILE__是C的内置宏,编译器编译的时候会填入对应的数据。delete的使用方式就不用讲了吧。下面是数组的分配释放函数

void *operator new[] (size_t nSize, int nLine, const char* szFile)
{
    return MemPool::GetInstance().Alloc((int) nSize, szFile, nLine, true);
}

void operator delete[](void *p)
{
    MemPool::GetInstance().Free(p);
}

和前面的operator new、operator delete的有啥区别?注意那个调用的Alloc的最后的参数,一个是false一个是true。翻翻前面的Alloc实现里面对这个参数做了啥?啥都没有啊!包括行号,文件名都没做任何操作。好吧,这个后面会加,泄漏和越界调试用的,现在先放一边吧。

下面说说清理函数了。我们知道,刚刚实现完毕的内存池(那个单模类)就是个静态类,程序启动时进行的构造和初始化,程序结束时进行析构。那么问题来了,假如我还有一个静态类,这个类能用这个内存池申请和释放它需要的内存么?比如下面这样

class test
{

private:
    char* m_pArray;
    static test m_instance;

    test() {
        m_pArray = NULL;
    };

public:
    ~test() { 
        if (m_pArray)
            delete[] m_pArray; 
    };

    void Init() { 
        m_pArray = new(__LINE__, FILE_NAME)[100]; 
        ...
    };

    static test& GetInstance() { return m_instance; };
};

test test::m_instance;  // 单模类实例
void main()
{
    ...
    test::GetInstance().Init();
    ...
}

问题很大。因为静态变量的构造和析构的次序是不定,除非都在一个源文件里。你不能保证内存池的销毁一定在其他静态类的后面。如果内存池已经销毁了,然后某个静态类在他的析构函数里释放了一个它从内存池申请的内存块,那就崩了。

怎么办?把上面这个类改一下

class test
{
...
public:
    ~test() { 
        Clear(); 
    };

    void Clear() {  // 释放相关内存
        if (m_pArray)
            delete[] m_pArray;
        m_pArray = NULL;
    }; 

    static void SomeCleanup(void *pParam) {  // 清理函数
        ((test*) pParam)->Clear();
    };

    void Init() { 
        MemPool::GetInstance().AddCleanup(SomeCleanup, this);  // 注册清理函数
        m_pArray = new(__LINE__, FILE_NAME)[100]; 
        ...
    };
...
};

注册清理函数的部分如下

void MemPool::AddCleanup(CleanupHandler pHandler, void* pParam)
{
    Cleanup *p = new Cleanup();
    p->pHandler = pHandler; p->pParam = pParam;
    p->pNext = m_pCleanups;
    m_pCleanups = p;
}

OK,现在内存池的实现基本完成了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值