- new与placement new
-
new:
- 先调用operator new(大小),而operator new()会调用malloc尝试分配内存,失败则调用_callnewh()来释放内存,直至分配成功
- 可以设置分配失败的处理函数:将写好的处理函数作为参数传入set_new_handler即可
- 然后将得到的指针转型
- 最后通过指针调用构造函数
- 先调用operator new(大小),而operator new()会调用malloc尝试分配内存,失败则调用_callnewh()来释放内存,直至分配成功
-
placement new
- 先调用operator new(大小, address),该带2个参数的函数直接返回buf,不做任何改动,即不分配内存。
- 然后将得到的指针转型
- 最后通过指针调用构造函数
-
总结:new会分配内存后调用构造函数,而placement new相当于只是调用构造函数。因此我们可以像下面这样模拟new的行为:
-
容器的内存分配方式
当元素放入容器时,容器也需要为其分配一块空间,但其不是用的new和delete,而是将其包装在construct()和destroy()函数中 -
设计一个内存池
- 重载局部的operator new()
-
方法1:单独多使用一些指针来将空闲块连成链表
下面在节点中使用next指针来将小块链接起来
void* A::operator new(size_t size) { cout << "局部new" << endl; A* p; if (!A::freeStore) { //申请一大块空间 //A::freeStore = (A*)malloc(size * Chunk); A::freeStore = reinterpret_cast<A*>(new char[Chunk*size]); p = A::freeStore;//初始化p指针也指向头节点 //串成链表 for (int i = 0; i < Chunk; i++) { p->next = p + 1;//地址增大4,就是一个A的大小 p = p->next; } p->next = NULL; } //把链表头部的空闲块拿出来用 p = A::freeStore; A::freeStore = A::freeStore->next; return p; }
-
使用嵌入式指针:也就是指针临时借用一下空闲块里的空间,等到需要分配出去使用时就正常使用即可。
下面的rep和next占用同一片空间
class A { private://数据 struct Airplane//占8字节 { int miles; char type; }; private: union { Airplane rep; A* next; }; static A* freeStore;//指向链表的头部的指针 static const int Chunk = 3;//内存池容纳几个 public: static void* operator new(size_t size); static void operator delete(void* ptr); void set(int m, char t).... void show().... };
-
- 重载局部的operator delete()
- 采用头插法,将空闲块插入到链表头
void A::operator delete(void* ptr) { cout << "局部delete" << endl; //头插法 static_cast<A*>(ptr)->next = A::freeStore; A::freeStore = static_cast<A*>(ptr); }
- 采用头插法,将空闲块插入到链表头
- c语言版本的内存池
- 先写个宏定义
#define malloc(mp, size) _malloc(mp, size) #define free(mp, ptr) _free(mp, ptr)
- 定义一个大的内存块,这里称其为页
typedef struct mempool_s { int block_size;//一小块的大小 int free_count;//该页内剩余空闲块的个数 void *mem;//指向该页首地址 void *ptr;//指向该页中最新创建的块的地址(即下次要分配出去内存的块) } mempool_t;
- 写一个内存池初始化函数,将128个小块串成链表
int memp_init(mempool_t *mp, size_t block_size) { printf("block_size: %ld\n", block_size); if (!mp) return -1; memset(mp, 0, sizeof(mempool_t)); mp->block_size = block_size; mp->free_count = MEM_PAGE_SIZE / block_size; mp->mem = malloc(MEM_PAGE_SIZE); if(!mp->mem) return -1; mp->ptr = mp->mem; //把该页中的128块串成链表 char *ptr = mp->ptr;//ptr指针指向第1块 for (int i = 0; i < mp->free_count; i++) { //使用2级指针:一级指针ptr,(char**)ptr将其强转为指向char*的指针, //*(char**)ptr为其指向的char*地址,将该地址赋值为ptr所指块的下一块地址 *(char**)ptr = ptr + block_size; ptr += block_size; } *(char**)ptr = NULL;//最后一块的前几个字节保存的是NULL return 0; }
- 先写个宏定义
- 最后写内存分配与释放的函数即可。释放时同样使用头插法
- 重载局部的operator new()
- G2.9 alloc的大致流程:page8