如何使用 boost::object_pool 和 std::unique_ptr?

How to use boost::object_pool with std::unique_ptr?

这是一个由以下代码说明的两部分问题:

#include <memory>
#include <vector>
#include <boost/pool/object_pool.hpp>

struct Foo {
   Foo(int i) : _i(i) {}
   void* operator new(size_t) = delete; // ***
   int _i;
 };

 using FooHandle = std::unique_ptr<Foo>;

 struct Bar {
    Foo* addFoo(int i) 
    {
      Foo* ptr = new (_fooPool.malloc()) Foo(i);             // 111
      return FooHandle(ptr, 
        &boost::object_pool<Foo,  
         boost::default_user_allocator_new_delete>::destroy); // 222
    }
    boost::object_pool<Foo> _fooPool;
 };

我正在努力确保

  1. Foo 类型的对象仅由 Bar 类型的对象分配和拥有,
  2. 它们存储在池中,
  3. 它们通过唯一指针访问。

使用Visual Studio编译器我运行陷入以下问题

  1. 如果我删除默认的 operator new(标有 *** 的行),则 placement new(标有 111 的行)不会编译。是我做错了什么还是VS限制?

  2. 我想,为了在池中分配适当的 unique_ptr,我需要提供对池删除器的访问权限。标记为 222 的行是我尝试这样做的。编译器也不接受它。正确的语法是什么?

boost::object_pool::destroy是一个非static成员函数,所以需要在object_pool实例上调用。此外,删除器是 unique_ptr 类型的一部分,因此必须相应地声明您的 FooHandle 别名。此外,您的 addFoo() 函数在应该返回 FooHandle 时返回 Foo*

using FooHandle = std::unique_ptr<Foo, std::function<void(Foo *)>>;

FooHandle addFoo(int i) 
{
  Foo* ptr = new (_fooPool.malloc()) Foo(i);
  return FooHandle(ptr, std::bind(&boost::object_pool<Foo>::destroy,
                                  &_fooPool, std::placeholders::_1));
}

new 表达式的问题是您的 deleted new 重载隐藏了全局放置 new 运算符。如果您还重载放置版本,代码将编译。

static void* operator new(size_t count, void *p)
{
    return ::operator new(count, p);
}

Live demo

或者,显式调用全局 operator new

Foo* ptr = ::new (_fooPool.malloc()) Foo(i);

您确定要处理已删除的 new 重载吗?这样做并不能阻止某人使用 ::new Foo(10) 在您的对象池之外分配 Foo


如T.C。评论中提到,使用 std::function 打包删除器远非理想。避免这种情况的一种方法是创建一个仿函数类型来存储对 object_pool 实例的引用,然后在该实例上调用 destroy

struct FooDeleter
{
    FooDeleter(boost::object_pool<Foo>& pool)
    : pool(&pool)
    {}

    void operator()(Foo *p)
    {
        pool->destroy(p);
    }
    boost::object_pool<Foo> *pool;
};

using FooHandle = std::unique_ptr<Foo, FooDeleter>;

FooHandle addFoo(int i) 
{
  Foo* ptr = _fooPool.malloc();
  if(ptr)
  {
      return FooHandle(::new (ptr) Foo(i), FooDeleter(_fooPool));
  }
  else
  {
      return FooHandle(nullptr, FooDeleter(_fooPool)); // or throw exception etc
  }
}

Live demo