Boost中object_pool使用案例及让construct()支持任意参数模板

12 篇文章 5 订阅

1 Boost::object_pool简介

特点:适用于类实例,功能和pool类似,析构自动释放内存池。而pool管理的简单数据类型,比如int、double
在这里插入图片描述

2 object_pool和pool的一些区别

除了上面提及的处理对象不一致,还有一个最大的不同

  1. object_pool有construct(),而pool并没有。
  2. construct()完成了两件事,1:调用malloc分配内存;2:调用类的构造函数进行初始化;所以推荐使用这种方式,也就不要自己调用malloc,之后再调用构造函数了
  3. 然而,construct()默认最多只有三个参数,需要处理下

construct()所在文件位置:
/usr/local/include/boost/pool/detail/pool_construct.ipp
这个文件是通过宏预处理m4生成的,下面分析.

这里先看看,construct()大致的实现过程

// 下面是一个四参数construct()
template <typename T0, typename T1, typename T2, typename T3>
element_type * construct(const T0 & a0, const T1 & a1, const T2 & a2, const T3 & a3)
{
  element_type * const ret = (malloc)();
  if (ret == 0)
    return ret;
  try { new (ret) element_type(a0, a1, a2, a3); }
  catch (...) { (free)(ret); throw; }
  return ret;
}

首先,需要注意的是object_pool只用于类对象上,因此element_type其实是一个类,进而element_type(a0, a1, a2, a3)其实是调用的类的四参构造函数

  1. 首先,通过内置的malloc分配内存。失败,发生异常,这种情况很少见,除非物理内存耗尽
  2. 之后调用对应参数个数的构造函数,这里使用的new的定位运算符,这里简单提下,因为这够一期博客了
  3. 返回初始化好的内存首地址—ret(placement new保证类的首地址一定在ret处)

预备知识之new定位运算符(placement new)

  1. 字面含义,placement–定位,就是再指定位置分配内存,不管是否被占用,
  2. 对于类,不像new智能,直接delete就会调用析构。不能使用delete,必须显示调用析构函数!!!

new定位运算符和new的区别

  1. new会在内存中查找空闲内存并返回首地址,而new(ret)暴力夺取ret开始后的内存(不管是否空闲)
  2. new分配的内存直接delete,而placement new必须显式调用析构函数!!!一定要注意这点,不然内存泄漏

总之,placement new既要程序员保证取得ret后的空间内存空闲,又要显示调用析构函数,为什么还要用呢?
答:placement new永远只从我们指定的地方分配,这就是存在的意义,我就要我的类地址在ret处。Boost使用大量这种技术。

因此,可以猜测destroy()函数的具体实现,结果也符合预期

  1. 显示调用析构函数
  2. 删除malloc分配的内存
    void destroy(element_type * const chunk)
    { //! Destroys an object allocated with \ref construct. 
      //!
      //! Equivalent to:
      //!
      //! p->~ElementType(); this->free(p);
      //!
      //! \pre p must have been previously allocated from *this via a call to \ref construct.
      chunk->~T();
      (free)(chunk);
    }

3 construct()支持任意参数模板

struct Demo
{
    int m_a, m_b, m_c, m_h;

    Demo(int x = 1, int y = 2, int z = 3, int h = 4) : m_a(x), m_b(y), m_c(z), m_h(h) {}
};

int main(int argc, char *argv[])
{
    boost::object_pool<Demo> p;
    auto p1 = p.construct(4, 5, 6, 7);
    assert(p1->m_a == 4 || p1->m_b == 5 || p1->m_c == 6); // 初始化成功
}

construct()其实是一族函数,默认最多三个参数,上面的案例中你是无法通过编译的,因为构造函数有四个参数,而默认最多三的参数,有两种办法,推荐第二种

  1. 利用脚本boost_1_74_0/boost/pool/detail/pool_construct.sh生成pool_construct.ipp,之后替换到库目录下,支持的参数越多,这个文件越大,不是太智能
  2. 利用C++11可变参数模板,定义一个支持任意多的辅助模板函数

3.1 pool_construct.sh脚本支持更多参数

位置:源码包下的boost_1_74_0/boost/pool/detail/,里面有一些工具:linux的sh脚本和Windows的bat脚本,下面以ubuntu为例。

// 提前安装好m4---不然会报错
sudo apt-get install m4

// 语法,运行前记得给+x权限
./pool_construct.sh N	// 生成最多支持N个参数construct()

// 把生成的pool_construct.ipp替换到库目录下
库目录:
/usr/local/include/boost/pool/detail/

如果需要在 windows下使用,也需要提前安装好m4,不然也是无法执行,因为Linux安装很简单,进而拷贝到windows也是没问题的。这里就不重述了

3.2 C++11可变参数模板

需要主义的是:

  1. 这种调用方式,第一个形参是object_pool对象,而后就是构造函数的形参

eg:

boost::object_pool<Demo> p;
auto p1 = construct(p, 4, 5, 6, 7);	// 和上面代码等价
  1. 这个函数可以随便放,就是要放在头文件中,模板都是这样的。或者放在pool_construct.ipp中,其他注释掉。
template <typename P, typename... Args>
inline typename P::element_type *cosntruct(P &p, Args &&... args)
{
    typename P::element_type *mem = p.malloc();

    assert(mem != 0);
    // 定位new表达式 placement new expression
    new (mem) typename P::element_type(std::forward<Args>(args)...); // 完美转发
}

4 Boost::object_pool总结

object_pool和pool的区别和联系

  1. object_pool有construct(),而pool并没有。
  2. construct()完成了两件事,1:调用malloc分配内存;2:调用类的构造函数进行初始化;所以推荐使用这种方式,也就不要自己调用malloc,之后再调用构造函数了
  3. 特点:适用于类实例,功能和pool类似,析构自动释放内存池。而pool管理的简单数据类型,比如int、double

如何理解construct()及其placement new?
C++11可变参数模板?

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
g_object_new_with_properties函数可以用于创建一个GObject实例,并且在实例化对象时可以设置该对象的属性值。它的原型如下: ```c gpointer g_object_new_with_properties (GType object_type, guint n_properties, GObjectConstructParam *properties); ``` 其object_type是要实例化对象的类型。n_properties是要设置的属性数量。properties是一个GObjectConstructParam类型的数组,用于保存要设置的属性信息。 下面是一个使用g_object_new_with_properties函数创建GObject实例并设置属性的示例代码: ```c #include <glib-object.h> typedef struct _MyObject MyObject; struct _MyObject { GObject parent_instance; gchar *name; gint age; }; enum { PROP_NAME = 1, PROP_AGE, N_PROPERTIES }; static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, }; static void my_object_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MyObject *self = MY_OBJECT (object); switch (property_id) { case PROP_NAME: g_free (self->name); self->name = g_value_dup_string (value); break; case PROP_AGE: self->age = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void my_object_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MyObject *self = MY_OBJECT (object); switch (property_id) { case PROP_NAME: g_value_set_string (value, self->name); break; case PROP_AGE: g_value_set_int (value, self->age); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void my_object_class_init (MyObjectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = my_object_set_property; gobject_class->get_property = my_object_get_property; obj_properties[PROP_NAME] = g_param_spec_string ("name", "Name", "The name of the object", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); obj_properties[PROP_AGE] = g_param_spec_int ("age", "Age", "The age of the object", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_properties (gobject_class, N_PROPERTIES, obj_properties); } static void my_object_init (MyObject *self) { self->name = NULL; self->age = 0; } int main (void) { MyObject *obj = g_object_new_with_properties (MY_TYPE_OBJECT, 2, "name", "Tom", "age", 18); g_object_unref (obj); return 0; } ``` 在上面的示例代码,我们定义了一个MyObject类型的GObject子类,并且添加了两个属性:name和age。在类初始化时,我们使用g_object_class_install_properties函数将这两个属性安装到MyObject。在对象初始化时,我们将这两个属性的默认值分别设置为NULL和0。 在main函数,我们使用g_object_new_with_properties函数创建一个MyObject实例,并且设置name属性为"Tom",age属性为18。最后,我们使用g_object_unref函数释放该实例。 需要注意的是,在使用g_object_new_with_properties函数创建对象时,属性名称和属性值必须成对出现,并且属性名称必须是字符串类型。在示例代码,我们使用"name"和"age"作为属性名称。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值