【问题标题】:How to use boost::object_pool with std::unique_ptr?如何将 boost::object_pool 与 std::unique_ptr 一起使用?
【发布时间】:2015-05-04 18:54:01
【问题描述】:

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

#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. 如果我删除默认操作符 new(标有 *** 的行),则放置 new(标有 111 的行)不会编译。我做错了什么还是 VS 限制?

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

【问题讨论】:

  • unique_ptr 的自定义删除器必须声明为模板参数并在构造函数中使用(与 shared_ptr 不同);所以你需要像using FooHandle = std::unique_ptr&lt;Foo, decltype( &amp;boost::object_pool&lt;Foo, boost::default_user_allocator_new_delete&gt;::destroy )&gt;这样的东西。
  • 另外你的addFoo函数需要返回一个FooHandle,而不是一个原始指针,否则一旦函数返回,对象就会被销毁。
  • 谢谢乔纳森·波特。当然你是对的,addFoo 必须返回 FooHandle。我试图这样做,但在处理其他问题时失去了最后的润色。

标签: c++ c++11 boost visual-studio-2013 unique-ptr


【解决方案1】:

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. cmets 中提到,使用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

【讨论】:

  • std::function 不是unique_ptr 的删除器的好选择;它的构造函数可以抛出。
  • @T.C.同意,我也不喜欢。我能想到的唯一其他选择是创建自己的仿函数,它采用 boost::object_pool&lt;Foo&gt;&amp; 构造函数参数。我还缺少其他选择吗?
  • boost::object_pool::malloc 在失败时返回nullptr::new (nullptr) Foo(...) 具有未定义的行为。
  • 谢谢大家。我应该能够根据这些建议烹饪我需要的东西。我确实意识到我需要使用特定池的破坏。但是,我返回 FooHandle(ptr, &_fooPool.destroy) 的天真尝试也无法编译,而且我也没有考虑使用 std::bind()。我删除了 non-placement new 以防止不了解整体意图的人在堆上意外创建 Foo 。但是没有考虑到有一个全球性的新解决方法。这可能仍然符合我的目的,因为我主要是为了防止事故发生,而不是阻止一个坚定的黑客。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-08-29
  • 2017-05-22
  • 1970-01-01
  • 1970-01-01
  • 2021-05-02
相关资源
最近更新 更多