我正在创建多个索引(即使用不同的键)到大量对象中。对象可以更改,集合可以缩小和增长。到目前为止我的想法:
保留多个指向对象的指针集合。 使用set而不是map来更好地封装。 使用unordered_set可以很好地扩展大型数据集。 理想情况下,指针应该是某种形式的智能指针。
我可以很容易地使用unique_ptrs的主集合开始,它管理所有分配,以及使用“原始”指针的二级索引(我暂时省略了支持函数,但请注意索引是一个multiset作为其密钥在整个集合中不是唯一的):
typedef boost::unordered_set< boost::unique_ptr<MyObject>,myobject_hash,myobjects_equal > MyObjects;
typedef boost::unordered_multiset<const MyObject*,myobject_index2_hash,myobject_index2_equal > MyObjectsIndex2;
用法很简单:
MyObjects my_objects;
MyObjectsIndex2 my_objects_index2;
auto it_mo = my_objects.insert(
boost::unique_ptr<MyObject>(
new MyObject(...)
)
);
const MyObject* p_mo = it_mo.first->get();
my_objects_index2.insert(p_mo);
我正在考虑付出额外的努力来替换索引对原始指针的使用以及对主集合的unique_ptrs的const引用。我不确定,但至少不容易。我想我会问其他人是否已经走过那条路,或者有其他建议。
更新
到目前为止的经验教训:
std::set<Key>::find
设置更好的功能。有关详细信息,请参阅here。因此,就目前而言,管理原始分配的数据存储似乎比尝试强制使用unique_ptr作为集合键或使用映射增加键空间存储更有意义。答案 0 :(得分:1)
这是一种方式。
std::vector<unique_ptr>
保存数据项(以确保在向量调整大小时地址不会改变),然后保存带有reference_wrappers(可复制引用)的容器以生成索引。
可编辑的例子:
#include <map>
#include <vector>
#include <set>
#include <string>
#include <functional>
#include <memory>
#include <iostream>
struct Thing {
Thing(std::string name, int value)
: _name { std::move(name) }
, _value { value }
{}
const std::string& name() const {
return _name;
}
void write(std::ostream& os) const {
os << "{ " << _name << " : " << _value << " }";
}
private:
std::string _name;
int _value;
};
inline std::ostream& operator<<(std::ostream& os, const Thing& t) {
t.write(os);
return os;
}
struct multi_index
{
using multi_by_name_index = std::multimap<std::string, std::reference_wrapper<Thing>>;
void add_thing(std::string name, int value) {
// todo: checks to ensure that indexes won't be violated
// add a new thing to the main store
_main_store.emplace_back(new Thing{std::move(name), value});
// store a reference to it in each index
auto& new_thing = *(_main_store.back().get());
_name_index.emplace(new_thing.name(), new_thing);
}
using multi_by_name_range = std::pair<multi_by_name_index::const_iterator, multi_by_name_index::const_iterator>;
multi_by_name_range get_all_by_name(const std::string name) const
{
return _name_index.equal_range(name);
}
private:
std::vector<std::unique_ptr<Thing>> _main_store;
std::multimap<std::string, std::reference_wrapper<Thing>> _name_index;
};
using namespace std;
int main()
{
multi_index mi;
mi.add_thing("bob", 8);
mi.add_thing("ann", 4);
mi.add_thing("bob", 6);
auto range = mi.get_all_by_name("bob");
for( ; range.first != range.second ; ++range.first) {
cout << range.first->second << endl;
}
return 0;
}
预期产出:
{ bob : 8 }
{ bob : 6 }
答案 1 :(得分:1)
我认识到你的用例可能与我为我的例子设计的用例不同,没有更多的细节,我赢得了无法匹配(我也认为如果你有很多您可以自己找到解决方案的详细信息。
#include <iostream>
#include <map>
#include <set>
#include <memory>
#include <stdexcept>
using namespace std;
class Thing
{
public:
Thing() = default;
Thing(const Thing &other) = default;
Thing(int i, string p, string d) : id(i), desc(d), part(p) {}
int id;
string desc;
string part;
};
ostream &operator<<(ostream &out, const Thing &t)
{
if (&t == NULL) out << "(NULL)"; // don't judge me
else out << t.id << ": " << t.part << " (" << t.desc << ")";
}
class Datastore
{
public:
Datastore() = default;
shared_ptr<const Thing> Add(const Thing &t)
{
if (!(index_bydesc.find(t.desc) == index_bydesc.end() &&
index_bypart.find(t.part) == index_bypart.end() &&
index_byid.find(t.id) == index_byid.end()))
throw runtime_error("Non-unique insert");
shared_ptr<const Thing> newt = make_shared<const Thing>(t);
weak_ptr<const Thing> weak = weak_ptr<const Thing>(newt);
index_bydesc[newt->desc] = weak;
index_bypart[newt->part] = weak;
index_byid[newt->id] = weak;
store.insert(newt);
return newt;
}
void Remove(const Thing &t)
{
shared_ptr<const Thing> p = FindBy_Desc(t.desc);
store.erase(p);
index_bydesc.erase(p->desc);
index_bypart.erase(p->part);
index_byid.erase(p->id);
}
shared_ptr<const Thing> FindBy_Desc(string desc)
{
map<string, weak_ptr<const Thing> >::iterator iter = index_bydesc.find(desc);
if (iter == index_bydesc.end()) return shared_ptr<const Thing>();
return iter->second.lock();
}
// index accessors for part and quantity omitted
private:
std::set<shared_ptr<const Thing> > store;
std::map<string, weak_ptr<const Thing> > index_bydesc;
std::map<string, weak_ptr<const Thing> > index_bypart;
std::map<int, weak_ptr<const Thing> > index_byid;
};
int main() {
Datastore d;
d.Add(Thing(1, "TRNS-A", "Automatic transmission"));
d.Add(Thing(2, "SPKPLG", "Spark plugs"));
d.Add(Thing(3, "HOSE-S", "Small hoses"));
d.Add(Thing(4, "HOSE-L", "Large hoses"));
d.Add(Thing(5, "BATT-P", "Primary battery (14.5v nominal)"));
d.Add(Thing(6, "BATT-S", "Secondary batteries (1.5v nominal)"));
d.Add(Thing(7, "CRKSFT", "Crank shaft"));
d.Add(Thing(8, "REAC-F", "Fusion reactor power source"));
cout << *d.FindBy_Desc("Crank shaft") << endl;
d.Remove(*d.FindBy_Desc("Crank shaft"));
cout << *d.FindBy_Desc("Crank shaft") << endl;
return 0;
}
list<Thing>
的地图保留为非唯一字段的索引,而不仅仅是包含Thing
的地图。std::map
相关的性能问题。 std::unordered_map
是对大型数据结构具有更好(常量摊销)访问时间的替代方法(与std::unordered_set
相同)。