内存池的基本概念是为您的应用程序分配大部分内存,然后,您不再使用 plainnew
从 O/S 请求内存,而是返回之前分配的内存块。
为了做到这一点,你需要自己管理内存使用,不能依赖O/S;即,您需要实现自己的new
and版本,delete
并且仅在分配、释放或可能调整您自己的内存池时使用原始版本。
第一种方法是定义自己的 Class 封装内存池并提供自定义方法来实现 and 的语义new
,delete
但从预分配的池中获取内存。请记住,这个池只不过是一个已分配的内存区域,new
并且具有任意大小。池的版本new
/ delete
return resp。采取指针。最简单的版本可能看起来像 C 代码:
void *MyPool::malloc(const size_t &size)
void MyPool::free(void *ptr)
您可以使用模板来自动添加转换,例如
template <typename T>
T *MyClass::malloc();
template <typename T>
void MyClass::free(T *ptr);
请注意,由于模板参数,size_t size
可以省略参数,因为编译器允许您调用sizeof(T)
.malloc()
返回一个简单的指针意味着您的池只能在有可用的相邻内存时增长,并且只有在其“边界”处的池内存未被占用时才会缩小。更具体地说,您不能重新定位池,因为这会使 malloc 函数返回的所有指针无效。
解决此限制的一种方法是返回指向指针的指针,即 returnT**
而不是 simple T*
。这允许您在面向用户的部分保持不变的情况下更改底层指针。顺便说一句,NeXT O/S 已经这样做了,它被称为“句柄”。要访问句柄的内容,必须调用(*handle)->method()
或(**handle).method()
。最终,Maf Vosburg 发明了一种伪运算符,它利用运算符优先级来摆脱(*handle)->method()
语法:handle[0]->method();
它被称为sprong 运算符。
此操作的好处是:首先,您避免了对new
and的典型调用的开销delete
,其次,您的内存池确保您的应用程序使用连续的内存段,即,它避免了内存碎片,因此增加了 CPU 缓存命中。
因此,基本上,内存池为您提供了一个加速,您可以通过可能更复杂的应用程序代码的缺点来获得。但是话又说回来,有一些内存池的实现已经过证明并且可以简单地使用,例如boost::pool。