【问题标题】:Incorrect vector size with Boost allocatorsBoost 分配器的向量大小不正确
【发布时间】:2018-01-10 06:41:09
【问题描述】:

以下程序在内存映射文件的空间内为cC 类型的对象)分配内存。向c 中包含的向量添加单个字符会将向量报告的大小从 0 更改为 18446744073709551520。

#include <iostream>
#include <new>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/offset_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/operations.hpp>

using namespace boost::interprocess;

class C;

typedef managed_mapped_file::segment_manager SegmentManager;
typedef allocator<void, SegmentManager> VoidAllocator;
typedef allocator<char, SegmentManager> CharAllocator;
typedef allocator<C, SegmentManager> CAllocator;
typedef offset_ptr<C> CPtr;

class C {
  public:
    std::vector<char, CharAllocator> data;

    C(const VoidAllocator &voidAlloc) : data(voidAlloc) {}

    void add_char() {
      std::cout << data.size() << std::endl;
      data.push_back('x');
      std::cout << data.size() << std::endl;
    }
};

int main(int argc, char *argv[]) {
  boost::filesystem::remove_all("file");
  managed_mapped_file segment(create_only, "file", 100000);

  VoidAllocator allocator_instance(segment.get_segment_manager());
  CAllocator alloc_c(allocator_instance);
  CPtr c = alloc_c.allocate_one();
  *c = C(allocator_instance);
  c->add_char();

  return 0;
}

c在堆栈上分配而不是动态分配时,不会出现该问题。

C c(allocator_instance);
c.add_char();

我在 Debian GNU/Linux 上使用 Boost 1.62 和 g++ 6.3.0-18 使用以下命令编译代码。

g++ -Wall -pthread  -lboost_system -lboost_filesystem t.cpp -o t

【问题讨论】:

    标签: c++ vector boost allocator


    【解决方案1】:

    分配器返回原始的、未初始化的内存。

    通过它间接指向C 类型的对象是Undefined Behaviour

    您当然可以使用placement-new 实际完成繁重的工作:

    CPtr c = alloc_c.allocate_one();
    new (&*c) C(allocator_instance);
    

    请注意,同样,对于非 POD(或者实际上,非平凡可破坏的类型),您还必须记住在适当的时间调用析构函数:

    CPtr c = alloc_c.allocate_one();
    
    new (&*c) C(allocator_instance);
    *c = C(allocator_instance);
    
    c->add_char();
    
    c->~C();
    alloc_c.deallocate_one(c);
    

    但正如您已经指出的那样,高级方法避免了新/删除并使用段管理器:

    CPtr c = segment.construct<C>("Name") (allocator_instance); 
    

    演示

    使用

    • find_or_construct 因此可以按名称检索共享对象(如果您不希望这样做,请使用 anonymousunique 实例;另请注意,您可以通过相同的名称实例化多个实例)

    • 使用allocator_type 有助于使用范围分配器适配器(想想:vector&lt;C&gt;

    • 参数化的C 使其易于与标准分配器和共享内存分配器一起使用

    • 使用段管理器指针和分配器实例的隐式转换

    • 隐藏实现细节offset_ptr(90% 的时间你不需要知道)

    Live On Coliru

    #include <boost/interprocess/allocators/allocator.hpp>
    #include <boost/interprocess/managed_mapped_file.hpp>
    #include <boost/interprocess/containers/vector.hpp>
    #include <iostream>
    
    namespace bip = boost::interprocess;
    
    namespace Shared {
        using Segment         = bip::managed_mapped_file;
        using Manager         = Segment::segment_manager;
        template <typename T = void>
            using Alloc       = bip::allocator<T, Manager>;
        template <typename T>
            using Vector      = bip::vector<T, Alloc<T> >;
    
        template <typename Alloc = std::allocator<void> >
        struct C {
            using allocator_type = Alloc;
            bip::vector<char, typename Alloc::template rebind<char>::other> data;
    
            C(Alloc alloc = {}) : data(alloc) {}
    
            void add_char() {
                std::cout << data.size() << std::endl;
                data.push_back('x');
                std::cout << data.size() << std::endl;
            }
        };
    }
    
    int main() {
        std::remove("file");
        Shared::Segment mmf(bip::create_only, "file", 1000000);
    
        using Alloc = Shared::Alloc<>;
        using C = Shared::C<Alloc>;
    
        auto* c = mmf.find_or_construct<C>("byname")(mmf.get_segment_manager());
    
        c->add_char();
    
        //mmf.destroy_ptr(c);
    }
    

    打印

    0
    1
    

    【讨论】:

    • 感谢您的详细回答,它解释了我看到的行为。您能否解释一下在哪里指定与原始内存交互,就好像它指向一个对象会导致未定义的行为?我认为添加新位置是为了优化,而不是为了防止未定义的行为。
    • 如果一个类型不是简单可构造的,将单元化内存重新解释为 T 类型的对象会破坏构造函数负责创建的不变量。特别是,在该对象上调用成员函数(如您的示例 operator=)可能会产生任何结果,包括吃掉邻居的狗,
    【解决方案2】:

    似乎将构造的对象从默认内存池移动到段一会以某种方式混淆向量的实现。使用construct方法直接在段内存池中构造对象即可解决问题。

    CPtr c = segment.construct<C>(anonymous_instance) (allocator_instance);
    

    【讨论】:

      猜你喜欢
      • 2017-11-16
      • 2015-01-09
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 2021-05-06
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多