巧用对象的机制构造局部堆自动清理内存助手

来源:互联网 发布:淘宝卖家遭遇诈骗 编辑:程序博客网 时间:2024/06/08 19:10


1 简说
本文是自己工作的一点经验和一些想法。
其实也不算什么新颖的技术,因为局部锁,局部光标画面等都是通过巧用对象的构造和析构来自动初始化和清理。本文所说也是。

欢迎高手和老手指点

2 问题的提出
 常常担心在代码中,有内存泄露的现象,所以不得不在函数体内的每个返回值做对内存的清理操作(或者一些goto,然后统一对内存清理)。对于在函数体内申请的内存块少的时候,代码短

小的,并没有什么,但是如果申请内存卡多了,总会影响一下代码的优美或者担心某块内存没有被释放。如果有个机制,使得我们申请的内存,自动释放,那是多么美好的事情!

3 初探
 封装一个类,用于作为内存块的管理助手,主要是构造时候帮忙申请内存,析构时候帮忙释放内存,这样就可以确保自己在函数体申请的内存在函数推出时候释放了,而不用当心有内存泄露


实现如下:

CLocalHeapHelper
{
public:
CLocalHeapHelper(void **p, unsigned int cbSize)
{
    if (cbSize == 0 || p == NULL || *p != NULL) { // 判断*p != NULL ,主要不对未初始化的指针或者已经申请过内存的指针进行操作。
        return;
    }

    m_ptr = malloc(cbSize);
    *p = m_ptr;   
}

~CLocalHeapHelper()
{
    if (m_ptr != NULL) {
        free(m_ptr);
         m_ptr = NULL;
    }
}

protected:
 void *m_ptr;
};

使用
void Test()
{
    STRUCT_1 *p_1 = NULL;
    CLocalHeapHelper Helper1((void **)&p_1, sizeof(STRUCT_1));
    if (p_1 == NULL) {
        return;
    }
    // dosomething

    STRUCT_2 *p_2 = NULL;
    CLocalHeapHelper Helper_2((void **)&p_2, sizeof(STRUCT_2));
    if (p_2 == NULL) {
        return;
    }
    // dosomething

    //……
    STRUCT_n *p_n = NULL;
    CLocalHeapHelper Helper_n((void **)&p_n, sizeof(STRUCT_n));
    if (p_n == NULL) {
        return;
    }
    // dosomething
}

这样 在函数返回,就不用担心在函数返回时候有泄漏内存了,也没有必要加个goto 然后Clear。保持了代码的整洁。

4 改进
 开始时候,在处理局部堆内存块时候,我是这样封过这样的类来用过,开始觉得挺不错的,但慢慢,在一些函数内部,申请的内存块比较多的是很,就不得不构造多个局部堆清理内存对象来

处理,细细想,其实是没有必要每块内存块都关联着一个对象的,所有内存块关联到一个对象里面去就可以了,只是那个对象要多维护一个链表。

再封装个类大概如下(实现在文章的最后 - 因为这不是重要的,各人根据需要来封装就行了):
class CLocalHeapMgr
{
public:
    CLocalHeapMgr();
    ~CLocalHeapMgr(); // 析构时候对说有通过本对象Malloc的内存块都释放

    void *Malloc(unsigned int cbSize);

public:
    void Free(void *ptr);// 释放已经清理过的ptr
    void ClearHeap();    // 释放CLocalHeapMgr所有申请过的内存块

protected:

    typedef struct _LOCAL_HEAP_ITEM {
        _LOCAL_HEAP_ITEM *pNext;
        void *ptr;
    } LOCAL_HEAP_ITEM, *PLOCAL_HEAP_ITEM;

    LOCAL_HEAP_ITEM m_head; // 浪费一个节点作为头节点,方便一下实现。
};

这样封装之后,上面的测试函数就可以进一步简化了:

void Test()
{
    CLocalHeapMgr lhHelper;

    STRUCT_1 *p_1 = lhHelper.Malloc(sizeof(STRUCT_1));
    if (p_1 == NULL) {
        return;
    }
    // dosomething

    STRUCT_2 *p_2 = lhHelper.Malloc(sizeof(STRUCT_2));
    if (p_2 == NULL) {
        return;
    }
    // dosomething

    //……
    STRUCT_n *p_n = lhHelper.Malloc(sizeof(STRUCT_n));
    if (p_n == NULL) {
        return;
    }
    // dosomething
}


其实  void Free(void *ptr);和  void ClearHeap();本来可以不要的,因为 如果加了这个函数,比如在函数体用了lhHelper.Malloc()了,还用用一下lhHelper.Free()或者

lhHelper.ClearHeap(),这样和自己申请,自己释放有什么区别呢?这个对象就有点多次一举了。

在开始封装这个类时候,也没有打算要这两个接口的,后来想,如果在一些循环内部申请内存的话(或者申请过多内存了,而且这些内存块也不用了的),清理一下也可以减少下进程占内存

的大小。所以就画蛇添足加了这两个接口。

5 存在问题
 就是因为public了void ClearHeap(); 这个接口,导致了一个隐瞒的小问题,比如

{
    ……
    CLocalHeapMgr lhHelper;

    STRUCT_1 *p_1 = lhHelper.Malloc(sizeof(STRUCT_1));
    // dosomething
    STRUCT_2 *p_2 = lhHelper.Malloc(sizeof(STRUCT_2));
    // dosomething

    lhHelper.ClearHeap();


    //……
    STRUCT_n *p_n = lhHelper.Malloc(sizeof(STRUCT_n));
    // dosomething
}

在调用lhHelper.ClearHeap()之后,如果不置p_1 p_2 为NULL,那么它们是野指针了的,如果忘记了又用的时候,不得了~~~~(>_<)~~~~ 。当然,调用Free 或者ClearHeap() 就应该确定之前

的指针都不能再用了的。

然而,也可以再修改封装类来解决这个问题,(有点感觉 类越封装,就越臃肿了~~)
就是在
CLocalHeapMgr的void *Malloc(unsigned int cbSize); 改成void Malloc(void **pAddr, unsigned int cbSize); 把申请的指针地址也一起关联到链表里面去,在LOCAL_HEAP_ITEM中再增加

一个节点来保存这个地址即可,在释放某块内存的时候,同时也把关联着的那个指针也置为NULL 就行了。这个就不去实现了,感觉那样封装,不好看^_^。


6 破代码:

CLocalHeapMgr::CLocalHeapMgr()
{
    m_head.pNext = NULL;
    m_head.ptr = NULL;
}

CLocalHeapMgr::~CLocalHeapMgr()
{
    this->ClearHeap();
}

void *CLocalHeapMgr::Malloc(unsigned int cbSize)
{
    if (cbSize == 0) {
        return NULL;
    }

    LOCAL_HEAP_ITEM *pNewItem = (LOCAL_HEAP_ITEM *)malloc(sizeof(LOCAL_HEAP_ITEM));
    if (pNewItem == NULL) {
        return NULL;
    }

    pNewItem->ptr = malloc(cbSize);
    if (pNewItem->ptr == NULL) {
        free(pNewItem);
        pNewItem = NULL;

        return NULL;
    }
    memset(pNewItem->ptr, 0, cbSize);
    pNewItem->pNext = m_head.pNext;
    m_head.pNext = pNewItem;

    return pNewItem->ptr;
}

void CLocalHeapMgr::Free(void *ptr)
{
    if (ptr == NULL) {
        return ;
    }
   
    LOCAL_HEAP_ITEM *pTraverItem = &m_head;
    LOCAL_HEAP_ITEM *pFreeItem = NULL;

    while (pTraverItem->pNext != NULL) {
        if (pTraverItem->pNext->ptr == ptr) {
            break;
        }

        pTraverItem = pTraverItem->pNext;
    }

    if (pTraverItem->pNext == NULL) { // not find
        return;
    }

    pFreeItem = pTraverItem->pNext;
    pTraverItem->pNext = pFreeItem->pNext;

    free(pFreeItem->ptr);
    free(pFreeItem);
    pFreeItem = NULL;
}

void CLocalHeapMgr::ClearHeap()
{
    LOCAL_HEAP_ITEM *pTraverItem = m_head.pNext;
    LOCAL_HEAP_ITEM *pTraverNext = NULL;
    m_head.pNext = NULL;

    while (pTraverItem != NULL) {
        pTraverNext = pTraverItem->pNext;
        free(pTraverItem->ptr);
        free(pTraverItem);
        pTraverItem = pTraverNext;
    }
}

原创粉丝点击