Boost程序库完全开发指南:深入C++”准”标准库(第5版)
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.8 object_pool

object_pool是用于类实例(对象)的内存池,它的功能与pool类似,但它会在析构时对所有已经分配的内存块调用析构函数,从而正确地释放资源。

object_pool位于名字空间boost,需要包含的头文件如下:

3.8.1 类摘要

object_pool的类摘要如下:

3.8.2 操作函数

object_pool是pool的子类,但它使用的是保护继承,因此不能使用pool的接口,但它们的基本操作还是很相似的。

object_pool的模板类型参数T指定了object_pool要分配的元素类型,要求其析构函数不能抛出异常。一旦在模板中指定了类型,object_pool实例就不能再用于分配其他类型的对象。

malloc()和free()函数分别分配和释放一块类型为T*的内存块,同样,可以用is_from()来测试内存块的归属,只有本内存池分配的内存才能被free()释放。但它们被调用时并不调用类的构造函数和析构函数,也就是说,操作的是一块原始内存块,里面的值是未定义的,因此我们应当尽量少使用malloc()和free()。

object_pool的特殊之处是construct()和destroy()函数,这两个函数是object_pool的真正价值所在。construct()实际上是一组函数,有多个参数的重载形式(目前最多支持3个参数,但可以扩展),它先调用malloc()分配内存,再在内存块上使用传入的参数调用类的构造函数,返回的是一个已经初始化的对象指针。destory()则先调用对象的析构函数,再用free()释放内存块。

这些函数都不会抛出异常,如果内存分配失败,将返回0(空指针)。

3.8.3 用法

object_pool的用法也很简单,我们既可以像使用pool那样使用object_pool分配原始内存块,也可以使用construct()直接在内存池中创建对象。当然,后一种使用方法是最方便的,也是本书所推荐的。

下面的代码示范了object_pool的用法:

3.8.4 更多的构造参数

在默认情况下,在使用object_pool的construct()的时候我们最多只能使用3个参数来创建对象。在大多数情况下这都是足够的,但有时我们可能会定义3个以上参数的构造函数,此时construct()的默认重载形式就不够用了。

很遗憾,construct()并没有及时跟进C++标准使用可变参数模板支持任意数量的参数构造。它基于宏预处理m4(通常UNIX系统自带,也有Windows的版本)实现了一个变通的扩展机制,可以生成接收任意数量参数的construct()函数代码。

pool库在目录boost/pool/detail下提供了两个名为pool_construct.m4和pool_construct_simple.m4的脚本,同时提供了可在UNIX和Windows操作系统下运行的同名sh和bat可执行脚本文件。只需要简单地向批处理脚本传递一个整数的参数N,m4就会自动生成能够创建具有N个参数的construct()函数的源代码。

例如,在Linux操作系统下,执行以下命令:

./pool_construct_simple.sh 5;./pool_construct.sh 5

执行上述命令将生成两个同名的.ipp文件,里面包含了新的construct()函数定义,最多能够支持传递5个参数创建对象。

m4的解决方案显得比较“笨拙”,使用C++11的可变参数模板特性,我们可以定义一个辅助模板函数,支持任意数量的参数,以彻底解决这个问题:

自由函数construct()可以接收任意数量的参数,第一个是object_pool对象,其后是创建对象所需的参数,要创建的对象类型可以使用object_pool的内部类型定义element_type来获得。该函数首先调用malloc()分配一块内存,然后调用不太常见的“定位new表达式”(placement new expression)来创建对象。

假设我们有如下的一个由4个参数组成的构造函数的类:

那么使用自定义的construct()创建对象的代码如下: