虽然boost::multi_index的声明形式比较特殊,但其确实定义了一个具有多个索引方式的容器!而且,和SQL数据库等类似,它也可以被定义出一个ordered_unique主键,以及多个非unique的其他索引键(ordered_non_unique)。
多索引容器的unique主键:当新项目被insert时,或者replace时,都要确保新值的主键字段值的唯一性;否则inset() 或 replace() 操作返回失败,新项目也无法插入/更新进去。
多索引容器的增加、删除、遍历和查找,操作都比较好理解!需要注意的是它的数据项的修改——修改数据之后,必须通过repalce()提交(个人猜测:在replace中实现对修改后的数据更新各个索引)。这也还是说,这个多索引容器和SQL数据库的操作类似!
至于具体的用法,可以参考下面的代码,自行编译、修改、体验一下!
#include <boost/multi_index_container.hpp> #include <boost/multi_index/hashed_index.hpp> #include <boost/multi_index/ordered_index.hpp> #include <boost/multi_index/sequenced_index.hpp> #include <boost/multi_index/member.hpp> #include <boost/multi_index/mem_fun.hpp> #include <iostream> #include <iterator> #include <utility>
//定义学生信息,同理可以使用结构定义 class student { public: int id; std::string name; int score; std::string remark; student(int id, std::string name, int score, std::string remark) :id(id), name(std::move(name)), score(score), remark(remark) { } void print() const { std::cout << "[student] id:" << id << ", name:" << name << ", score:" << score << std::endl; } }; // 如果要把student某个属性字段设置为搜索引擎,则需要定义用于排序的空结构体对象 struct _id {}; struct _name{}; struct _score {}; // 定义一个多索引容器——— 它的底层结构是用来存放 结构体的 map 容器 TODO 这个和vector等一样,也是容器!!! using student_table = boost::multi_index::multi_index_container< student, ///此处为你要排序的成员类名,即"容器"针对哪个数据类! boost::multi_index::indexed_by< boost::multi_index::ordered_unique< boost::multi_index::tag<_id>, BOOST_MULTI_INDEX_MEMBER(student, int, id) >, // ID为唯一索引,类似SQL主键(向本容器中插入新数据时不得冲突) boost::multi_index::ordered_non_unique< boost::multi_index::tag<_name>, BOOST_MULTI_INDEX_MEMBER(student, std::string, name) >, // 非唯一索引 boost::multi_index::ordered_non_unique< boost::multi_index::tag<_score>, BOOST_MULTI_INDEX_MEMBER(student, int, score) > > >; // 定义完成! int boostMultiIndexTest() { // initialize data. student_table allStu; // 插入数据 --- TODO 注意:这个「多索引容器」是一个链式结构(有left/right二叉树式指针) allStu.insert(student(3, "xiao", 65, "hello")); allStu.insert(student(2, "limi", 90, "hello")); allStu.insert(student(1, "lily", 85, "hello")); allStu.insert(student(4, "er狗", 80, "hello")); allStu.insert(student(5, "Joan", 60, "hello")); std::cout << "\n插入时已经默认按照「ordered_unique主键」进行排序了:" << std::endl; for (const auto& iter : allStu) { iter.print(); } // sort std::cout << "\nsort by student id(第一个索引):" << std::endl; student_table::index<_id>::type& sort_id = allStu.get<_id>(); ///通过tag<>内的名字,或索引序号,都OK! student_table::index<_id>::type::iterator iter_id = sort_id.begin(); for (; iter_id != sort_id.end(); iter_id++) { iter_id->print(); } std::cout << "\nsort by student score(第三个索引):" << std::endl; student_table::index<_score>::type& sort_score = allStu.get<2>(); student_table::index<_score>::type::iterator iter_score = sort_score.begin(); for (; iter_score != sort_score.end(); iter_score++) { iter_score->print(); } std::cout << "\nsort by student name(第二个索引):" << std::endl; student_table::index<_name>::type& sort_name = allStu.get<1>(); student_table::index<_name>::type::iterator iter_name = sort_name.begin(); for (; iter_name != sort_name.end(); iter_name++) { iter_name->print(); } // 查找 std::cout << "\n通过索引来查找指定项(并可修改条目的数据):" << std::endl; student_table::index<_name>::type& find_name = allStu.get<_name>(); student_table::index<_name>::type::iterator found_item = find_name.find("er狗"); if (found_item != find_name.end()) { //不是end,就是找到了! std::cout << "—— found a student named 「er狗」:" << std::endl; found_item->print(); ///可以在找到之后直接修改 ———— 注意如果是多任务系统,注意要用lock!!! student new_ergou = *found_item; new_ergou.id = 0; //TODO 这个id是unique索引Key,可以保持原值,或者其他不能和既有项冲突的值!否则修改不成功! new_ergou.name = "二狗"; new_ergou.score = 91; new_ergou.remark = "hello 二狗"; find_name.replace(found_item, new_ergou); ///实施修改!!! } std::cout << "\n数据修改之后,再次遍历索引 --sort by student name(第二个索引):" << std::endl; //student_table::index<_name>::type& sort_name = allStu.get<1>(); student_table::index<_name>::type::iterator new_iter_name = sort_name.begin(); for (; new_iter_name != sort_name.end(); new_iter_name++) { new_iter_name->print(); } std::cout << "---------------------------------------------------------\n" << std::endl; // 和SQL一样,插入新项时,unique主键不能冲突!!! std::cout << "新增一项,项中数据为:" << std::endl; std::pair<student_table::iterator, bool> ret = allStu.insert(student(9, "kent", 99, "man")); ret.first->print(); //迭代,就是指代一个元素,Java中也是如此! std::cout << "新增一项,插入成功否?" << ret.second << std::endl; //bool值 //遍历核实: std::cout << "新增一项,再次遍历如下(核实):" << std::endl; student_table::index<_name>::type::iterator new2_iter_name = sort_name.begin(); for (; new2_iter_name != sort_name.end(); new2_iter_name++) { new2_iter_name->print(); } std::cout << "---------------------------------------------------------" << std::endl; }