C++给类实现内存池的功能

1、为什么需要重写operator new 和operator delete

默认的operator new 具有很好的通用性,可以分配任意大小的内存块。operator delete也可以释放任意大小的内存块。为了让operator delete弄清楚需要释放多大内存,通常会在operator new分配的内存块里附带一些额外信息,用来指明被分配内存块的大小,所以分配内存的大小实际比存储对象所需内存大一点。就因为这种通用性及灵活性,导致在那些需要动态分配大量小的对象的应用程序中可以改善性能,因此在这种情况下为了效率有必要重写

2、内存池

概念及实现:

内存池就是在真正使用内存之前,先申请分配一定数量的、大小相等(一般情况下)的内存块留作备用。当有新的内存需求时,就从内存池中分出一部分内存块,若内存块不够再继续申请新的内存。这样做的一个显著优点是,使得内存分配效率得到提升,特别对于小内存分配的时候可以起到减少内存碎片的作用。
实现思路:先让缺省operator new 分配一些大块的原始内存,每块的大小都足以容纳很多个小内存对象。 小内存对象的内存块就取自这些大的内存块。当前没被使用的内存块被组织成链表(自由链表)以备未来小内存对象使用。当动态分配小对象时候,将某个小块内存从自由链表中移除客户并得到此块内存首地址;当客户删除对象时,小块内存放回自由链表。
区别与内存泄漏:
内存泄漏对应于内存分配后指向内存的指针丢失了,客户再也不能使用这块内存了。此处分配内存全部串联在链表之中,客户需要使用时候全部可以使用,只是暂时没有释放而已,以备后续对象动态分配,所以这个称之为内存的池。

简单实现

#ifndef MEMORYPOOL_H
#define MEMORYPOOL_H

#include<new>
#include<iostream>

class MemoryPool
{
public:
    MemoryPool(size_t n);//构造函数,构造自由链表
    void *alloc(size_t n);//分配函数,从自由链表调出一个小块
    void free(void *__ptr , size_t n);//释放函数,内存块加入自由链表
    ~MemoryPool();//析构函数
    int GetChunks(){
        return AvailableChunks;
    }

private:
    static const int BLOCK_SIZE;//声明常量,初始化节点个数
    union obj{//联合体内部成员不可能同时使用
        union obj *next;//自由链表使用
        char client_data[1];//用户使用
    };
    union obj *headOfFreeList;
    int  AvailableChunks = 0;//返回自由链表中可用节点的个数
};

#endif // MEMORYPOOL_H
#include "memorypool.h"

const int MemoryPool::BLOCK_SIZE = 5;//定义常量

MemoryPool::MemoryPool(size_t n)//直接构造自由链表
{
    //自由链表为空,分配可以容纳BLOCK_SIZE个的n块
    union obj *current_obj , *next_obj;
    char *chunk =  (char *)malloc(BLOCK_SIZE * n);//分配 BLOCK_SIZE*n 大内存
    headOfFreeList = current_obj = next_obj =  (union obj *)chunk;//首地址
    for(int i = 0 ; i < BLOCK_SIZE - 1 ;i++){//将内存块组成自由链表
        next_obj = (union obj *)((char *)next_obj + n);//找出下一个节点地址
        current_obj->next = next_obj;//存储下一个节点地址
        current_obj = next_obj;//更新当前节点,为下次做准备
    }
    AvailableChunks = BLOCK_SIZE;
    current_obj->next = 0;//尾部节点置为空。
}

void *MemoryPool::alloc(size_t n){//分从自由链表分配一个节点
    union obj *result = headOfFreeList;
    union obj *current_obj , *next_obj;
    if(result){//如果为空,那么就是没有了
        headOfFreeList = headOfFreeList->next;
        AvailableChunks--;
    }
    else{//从堆中补充自由链表,注意第一个地址返回
        char *chunk =  (char *)malloc(BLOCK_SIZE * n);//分配 BLOCK_SIZE*n 大内存
        result = (union obj *)chunk;
        headOfFreeList = current_obj = next_obj =  (union obj *)(chunk+n);//首地址
        for(int i = 1 ; i < BLOCK_SIZE - 1 ;i++){//将内存块组成自由链表
            next_obj = (union obj *)((char *)next_obj + n);//找出下一个节点地址
            current_obj->next = next_obj;//存储下一个节点地址
            current_obj = next_obj;//更新当前节点,为下次做准备
        }
        current_obj->next = 0;//尾部节点置为空。
        AvailableChunks = BLOCK_SIZE - 1;
    }
    return result;//返回

}
void MemoryPool::free(void *__ptr , size_t n){//释放一个节点,加入链表头即可
    union obj *p = (union obj *)__ptr;
    p->next = headOfFreeList;
    headOfFreeList = p;
    AvailableChunks++;
}

MemoryPool::~MemoryPool(){

}
#include "memorypool.h"
#include<iostream>
using std::cout;
using std::endl;
class Airplane{
public:
    Airplane():AirplaneRep(4){}
    static void *operator new(size_t size);
    static void operator delete(void *p  , size_t size);
    int val(){
        return AirplaneRep;
    }
    static int Chunks(){
        return memPool.GetChunks();
    }

private:
    int AirplaneRep,i ,j, k;
    static MemoryPool memPool;//声明一个静态对象
};
void *Airplane::operator new(size_t size){
    //cout << "operator new" << endl;
    return memPool.alloc(size);
}

void Airplane::operator delete(void *p  , size_t size){
    memPool.free(p , size);
}
MemoryPool Airplane::memPool(sizeof(Airplane));//定义并通过构造初始化

int main(void)
{
    int i = 9;
    Airplane *ptr = new Airplane;//分配内存并调用默认构造
    cout << Airplane::Chunks() << endl;//返回可用chunks(内存块)
    Airplane *ptr1 = new Airplane;//分配内存并调用默认构造
    cout << Airplane::Chunks() << endl;
    Airplane *ptr2 = new Airplane;//分配内存并调用默认构造
    cout << Airplane::Chunks() << endl;
    Airplane *ptr3 = new Airplane;//分配内存并调用默认构造
    cout << Airplane::Chunks() << endl;
    Airplane *ptr4 = new Airplane;//分配内存并调用默认构造
    cout << Airplane::Chunks() << endl;

    Airplane *ptr5 = new Airplane;
    cout << Airplane::Chunks() << endl;

    delete ptr5;
    cout << Airplane::Chunks() << endl;

    delete ptr4;
    cout << Airplane::Chunks() << endl;

    delete ptr3;
    cout << Airplane::Chunks() << endl;
    return 0;
}

这里写图片描述
初始化默认小块的个数为5,程序运行首先调用静态成员memPool的构造函数Airplane::memPool(sizeof(Airplane));初始化自由链表。上图是某次允许时自由链表的构造情况,此处内存池首地址headOfFreeList = 0x018b6c20sizeof(Airplane) = 16,所以内存池的首地址为0x018b6c20,尾地址为0x018b6c6f。并且本主机是64位小端机器,每一个地址变量占用8字节,union obj对象占用8字节。经过自由链表之后,可以清楚的看到第一块上面存放的是第二块的地址,第二块存放第三块的地址,第三块存放第四块的地址,第四块存放第五块的地址,第五块为链表末尾置为0
注意:阅读STL源代码内存分配的时候,自由链表节点大小分别进行了8字节对齐。因为union obj对象占用8字节,所以一个块至少需要8字节,才可以存放一个union obj *指针用来指向下一个块。所以此处sizeof(Airplane)至少等于8,所以在Airplane类中多声明了几个int变量,否则会出现malloc报错,因为分配的内存不足以存放union obj指针变量。

这里写图片描述
调用一次new Airplane,Chunks数量减少一个;delete AirplaneChunks数量增加一。特别注意此处默认的BLOCK_SIZE为5比较小,所以Chunks为0继续new Airplane会重新生成内存池,然后释放先前分配的Airplane对象,此时Chunks的值可能大于默认的BLOCK_SIZE为5,例如上面出现了7。为了避免这种情况发生可以让BLOCK_SIZE稍微大一点,使得Chunks为0的概率小点。
STL源代码利用了自由链表池,产生了16个自由链表,分别对应8,16,24,32,40,48,56,64,72,80,88,96,104,112,120,128字节小内存块。思路和上面的实现完全一样,后期会剖析STL源代码里面的内存分配功能。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
最坏适应算法(Worst Fit Algorithm)是内存分配的一种策略,该算法将请求的内存块分配给最大的可用空闲区域。 C++通过malloc和free函数提供了动态内存分配和释放功能。下面是使用最坏适应算法实现内存的分配和回收的示例代码: ```c++ #include <iostream> #include <cstdlib> #include <cstring> #define MEM_SIZE 1024 // 内存池大小 struct memory_block { int size; // 块大小 bool free; // 是否空闲 memory_block *next; // 下一个空闲块 }; memory_block *start = (memory_block*) malloc(MEM_SIZE); // 内存池起始地址 void initialize_memory() { start->size = MEM_SIZE - sizeof(memory_block); // 初始空闲块大小 start->free = true; // 初始块为空闲 start->next = NULL; // 初始块为最后一个块 } void *worst_fit_allocate(int size) { memory_block *current = start; memory_block *worst_fit = NULL; while (current != NULL) { if (current->free && current->size >= size) { // 找到空闲块 if (worst_fit == NULL || current->size > worst_fit->size) { // 找到最大的可用空闲区域 worst_fit = current; } } current = current->next; } if (worst_fit == NULL) { // 没有找到合适的块 return NULL; } if (worst_fit->size == size) { // 找到了刚好合适的块 worst_fit->free = false; return (void*)(worst_fit + 1); } else { // 找到了大于所需的块,需要将多余的空间分裂成新的块 memory_block *new_block = (memory_block*)((char*)worst_fit + sizeof(memory_block) + size); new_block->size = worst_fit->size - sizeof(memory_block) - size; new_block->free = true; new_block->next = worst_fit->next; worst_fit->size = size; worst_fit->free = false; worst_fit->next = new_block; return (void*)(worst_fit + 1); } } void worst_fit_free(void *ptr) { if (ptr == NULL) { return; } memory_block *current = (memory_block*)ptr - 1; current->free = true; // 合并连续的空闲块 memory_block *prev = NULL, *next = start; while (next != NULL) { if (prev != NULL && prev->free && next->free) { // 合并前一个块和当前块 prev->size += sizeof(memory_block) + next->size; prev->next = next->next; next = prev->next; } else { // 不需要合并,继续查找下一个块 prev = next; next = next->next; } } } int main() { initialize_memory(); // 分配内存 int *a = (int*)worst_fit_allocate(100 * sizeof(int)); int *b = (int*)worst_fit_allocate(200 * sizeof(int)); int *c = (int*)worst_fit_allocate(50 * sizeof(int)); int *d = (int*)worst_fit_allocate(300 * sizeof(int)); // 输出分配的地址 std::cout << "a: " << a << std::endl; std::cout << "b: " << b << std::endl; std::cout << "c: " << c << std::endl; std::cout << "d: " << d << std::endl; // 释放内存 worst_fit_free(b); worst_fit_free(d); // 再次分配内存 int *e = (int*)worst_fit_allocate(150 * sizeof(int)); int *f = (int*)worst_fit_allocate(100 * sizeof(int)); // 输出分配的地址 std::cout << "e: " << e << std::endl; std::cout << "f: " << f << std::endl; free(start); return 0; } ``` 在该示例中,使用一个链表来管理内存块,每个内存块包含块大小、是否空闲和下一个空闲块的指针。initialize_memory函数初始化内存池,worst_fit_allocate函数使用最坏适应算法分配内存,worst_fit_free函数释放内存,并在必要时合并相邻的空闲块。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

有时需要偏执狂

请我喝咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值