背景
需要 1 个容器,容器需要具备以下功能:
- 能按某字段(如积分)插入元素自动排序
- 能按某字段(如ID)快速访问元素
- 能快速遍历以某元素为中心的某区域元素
- 使用 C++ 语言
实现思路
这里涉及多个基础容器的功能的一个组合:
- map,按 id 快速访问元素
- priority_queue,大、小堆,按值自动排序
- list,前后遍历
通常,我们可能自己手动组合上面的容器,来实现背景中提到的功能
这里介绍下 boost::multi_index 可以很简单的实现之
boost::multi_index
官方文档:
https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/index.html
官方例子:
https://www.boost.org/doc/libs/1_68_0/libs/multi_index/doc/examples.html
实现例子
可以参见本人实现的:
https://github.com/fananchong/multi_index_example
这里摘录下使用代码:
#include <multi_index.hpp>
#include <time.h>
#include <stdlib.h>
struct MyData
{
int aaa;
friend std::ostream& operator<<(std::ostream& os, const MyData& e)
{
os << e.aaa;
return os;
}
};
using MyContainer = Container<MyData>;
using MyScore = Score<MyData>;
float myrand()
{
return rand() / (RAND_MAX + 1.0);
}
int main()
{
srand(time(0));
MyContainer c;
c.insert(MyScore{ 1, 1000, MyData{1} });
c.insert(MyScore{ 3, 500, MyData{2} });
c.insert(MyScore{ 4, 600, MyData{3} });
c.insert(MyScore{ 5, 700, MyData{4} });
c.insert(MyScore{ 0, 550, MyData{5} });
c.insert(MyScore{ 2, 1500, MyData{6} });
for (int i = 0; i < 10; i++)
{
auto item = c.get_one(3, 500, myrand);
printf("%lu %lu %lu\n", item->id, item->score, item->data.aaa);
}
for (int i = 0; i <= 5; ++i)
{
c.erase(i);
}
printf("c size: %d\n", c.size());
}
封装代码:
#pragma once
#if !defined(NDEBUG)
#define BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
#define BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
#endif
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <functional>
#include <vector>
using boost::multi_index_container;
using namespace boost::multi_index;
template<class T>
struct Score
{
uint64_t id;
int64_t score;
T data;
Score(uint64_t id_, int score_, const T& data_)
: id(id_)
, score(score_)
, data(data_)
{}
friend std::ostream& operator<<(std::ostream& os, const Score& e)
{
os << e.id << " " << e.score << " " << e.data << std::endl;
return os;
}
};
struct id {};
struct score {};
template<class T>
class Container : public multi_index_container <Score<T>, indexed_by <
ordered_unique <tag<id>, BOOST_MULTI_INDEX_MEMBER(Score<T>, uint64_t, id)>,
ordered_non_unique <tag<score>, BOOST_MULTI_INDEX_MEMBER(Score<T>, int64_t, score)>>
>
{
public:
const Score<T>* get_one(uint64_t id_, int64_t range, std::function<float()> random_func)
{
auto it = this->get<id>().find(id_);
if (it == this->get<id>().end())
{
return nullptr;
}
auto s = it->score;
auto it_upper = this->get<score>().upper_bound(s - range);
auto it_lower = this->get<score>().lower_bound(s + range);
std::vector<const Score<T>*> temps;
for (auto it = it_upper; it != it_lower; ++it)
{
temps.push_back(&(*it));
}
if (temps.size() > 0)
{
int index = int(random_func() * temps.size());
return temps[index];
}
return nullptr;
}
};
template<typename Tag, typename MultiIndexContainer>
void print_out_by(const MultiIndexContainer& s)
{
const typename boost::multi_index::index<MultiIndexContainer, Tag>::type& i = get<Tag>(s);
typedef typename MultiIndexContainer::value_type value_type;
std::copy(i.begin(), i.end(), std::ostream_iterator<value_type>(std::cout));
}