Boost C++ 库

...世界上最受尊敬和专家设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

Boost.MultiIndex 教程:容器创建



目录

值语义

multi_index_container 具有与复制构造和赋值相关的常用值语义,即从源容器创建元素副本并将其插入目标容器。更有趣的是,复制还会重新创建元素在容器的*每个索引*中排列的原始顺序。这意味着,对于定义了相等性的索引类型,在复制或赋值下会保留所有索引的相等性。这种行为可以看作是对一般复制语义规则的自然扩展,该规则指出如果 `y` 是 `x` 的副本,则 `y==x`。

使用 ctor_args_list

虽然在大多数情况下,multi_index_container 将默认构造(或从预先存在的 multi_index_container 复制),但有时需要为所使用的内部对象(键提取器、比较谓词、分配器)指定特定值,例如,如果其中一些对象没有默认构造函数。标准 STL 容器也会出现同样的情况,它们允许可选地指定这些对象。

// example of non-default constructed std::set
template<typename IntegralType>
struct modulo_less
{
  modulo_less(IntegralType m):modulo(m){}

  bool operator()(IntegralType x,IntegralType y)const
  {
    return (x%modulo)<(y%modulo);
  }

private:
  IntegralType modulo;
};

typedef std::set<unsigned int,modulo_less<unsigned int> > modulo_set;

modulo_set m(modulo_less<unsigned int>(10));

multi_index_container 也提供了此功能,但由于 multi_index_container 的构造函数必须接受其索引所有内部对象的值,因此其方式要复杂得多。multi_index_container 构造函数的完整形式是

explicit multi_index_container(
    const ctor_args_list& args_list=ctor_args_list(),
    const allocator_type& al=allocator_type());

分配器对象的规范没有特殊问题;至于 ctor_args_list,该对象旨在保存 multi_index_container 中每个索引的必要构造值。从用户的角度来看,ctor_args_list 等效于类型

boost::tuple<C0,...,CI-1>

其中 `I` 是索引的数量,而 `Ci` 是

nth_index<i>::type::ctor_args

即第 `i` 个索引的嵌套类型 ctor_args。每个 ctor_args 类型又是一个元组,其中包含关联索引的构造函数参数的值:因此,有序索引需要一个键提取器对象和一个比较谓词,哈希索引需要初始桶数、键提取器、哈希函数和相等谓词;而序列和随机访问索引不需要任何构造参数。例如,给定定义

typedef multi_index_container<
  unsigned int,
  indexed_by<
    hashed_unique<identity<unsigned int> >,
    ordered_non_unique<identity<unsigned int>, modulo_less<unsigned int> >,
    sequenced<>,
    random_access<>
  >
> modulo_indexed_set;

相应的 ctor_args_list 类型等效于

boost::tuple<
  // ctr_args of index #0
  boost::tuple<
    std::size_t, // initial number of buckets; 0 if unspecified
    identity<unsigned int>,
    boost::hash<unsigned int>,
    std::equal_to<unsigned int> >,    

  // ctr_args of index #1
  boost::tuple<
    identity<unsigned int>,
    modulo_less<unsigned int> >,
  
  // sequenced indices do not have any construction argument
  boost::tuple<>,

  // neither do random access indices
  boost::tuple<>
>

这样的 modulo_indexed_set 不能默认构造,因为 modulo_less 没有提供默认构造函数。以下显示了如何进行构造

modulo_indexed_set::ctor_args_list args_list=
  boost::make_tuple(
    // ctor_args for index #0 is default constructible
    modulo_indexed_set::nth_index<0>::type::ctor_args(),
    
    boost::make_tuple(identity<unsigned int>(),modulo_less<unsigned int>(10)),
    
    // these are also default constructible (actually, empty tuples) 
    modulo_indexed_set::nth_index<2>::type::ctor_args(),
    modulo_indexed_set::nth_index<3>::type::ctor_args()
  );

modulo_indexed_set m(args_list);

示例部分提供了一个程序,用于实践这些概念。

特殊分配器支持

Boost.MultiIndex 允许使用比 C++ 标准严格要求的更通用的分配器类,如参考中详细说明的那样。Boost 进程间库提供的分配器是一种重要的非标准分配器;这开启了将 multi_index_container 放置在共享内存中的可能性。

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

namespace bip=boost::interprocess;

// a shared memory compatible allocator of ints
typedef bip::allocator<
  int,bip::managed_shared_memory::segment_manager
> shared_int_allocator;

// define a shared memory compatible multi_index_container
// using shared_int_allocator
typedef multi_index_container<
  int,
  indexed_by<
    sequenced<>,
    ordered_unique<identity<int> >
  >,
  shared_int_allocator
> unique_int_list;

...

// create a managed memory segment
bip::managed_shared_memory seg(
  bip::create_only,"SharedMemoryID",65536);

// construct a unique_int_list into the segment
unique_int_list* puil=seg.construct<unique_int_list>
  ("UniqueIntListID") // object identifier within the segment
  // Construction args: first a ctor arg list, then a
  // shared memory allocator obtained from the segment object.
  (unique_int_list::ctor_args_list(),
   unique_int_list::allocator_type(seg.get_segment_manager()));
   

示例部分包含一个进一步探讨此功能的程序

序列化

可以使用Boost 序列化库multi_index_container 进行存档和检索。支持常规和 XML 存档。用法简单,与其他任何可序列化类型没有什么不同。例如

#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <fstream>

...

void save(const employee_set& es)
{
  std::ofstream ofs("data");
  boost::archive::text_oarchive oa(ofs);
  oa<<es;
}

void load(employee_set& es)
{
  std::ifstream ifs("data");
  boost::archive::text_iarchive ia(ifs);
  ia>>es;
}

...

employee_set es;
... // fill it with data
save(es);

...

employee_set restored_es;
load(restored_es);

只需链接相应的 Boost.Serialization 库模块即可自动提供序列化功能:除了声明序列化过程中使用的存档类型的头文件外,无需显式包含 Boost.Serialization 中的任何头文件。但是,如果不使用序列化支持,可以通过全局定义宏 BOOST_MULTI_INDEX_DISABLE_SERIALIZATION 来禁用它。禁用 Boost.MultiIndex 的序列化可以稍微缩短构建时间,并且对于那些无法正确处理 Boost.Serialization 头文件的缺陷编译器可能是必要的。

根据 Boost.MultiIndex 的值语义,检索存档的 multi_index_container 不仅会恢复元素,还会恢复它们在容器每个索引中的排列顺序。但是,此规则有一个例外:对于哈希索引,不保证在恢复的容器中迭代元素的顺序;通常,依赖哈希索引元素的顺序是不明智的,因为它在插入或重新哈希期间可能会以任意方式更改——这正是哈希索引和 TR1 无序关联容器不定义相等运算符的原因。

还可以序列化 multi_index_container 索引的迭代器。迭代器的序列化必须在序列化其对应的容器之后进行。

示例部分中的示例 9 展示了 Boost.MultiIndex 的序列化功能。




修订于 2007 年 7 月 17 日

© 版权所有 2003-2007 Joaquín M López Muñoz。根据 Boost 软件许可证,版本 1.0 分发。(请参阅随附文件 LICENSE_1_0.txt 或复制于 https://boost.ac.cn/LICENSE_1_0.txt