Create multiple indexes into a large collection of objects with smart pointers

前端 未结 2 1313
粉色の甜心
粉色の甜心 2021-01-16 18:15

I am creating multiple indexes (ie, that use different keys) into a large collection of objects. The objects can change, and the collection can shrink and grow. My thought

2条回答
  •  隐瞒了意图╮
    2021-01-16 18:46

    I recognize that your use case is probably different from the one I contrived for my example, and without a lot more details I won't be able to make one that matches (I also think that if you had a lot of details you would be able to find a solution yourself).

    #include 
    #include 
    #include 
    #include 
    #include 
    
    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 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 newt = make_shared(t);
            weak_ptr weak = weak_ptr(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 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 FindBy_Desc(string desc)
        {
            map >::iterator iter = index_bydesc.find(desc);
            if (iter == index_bydesc.end()) return shared_ptr();
            return iter->second.lock();
        }
    
        // index accessors for part and quantity omitted
    
    private:
        std::set > store;
    
        std::map > index_bydesc;
        std::map > index_bypart;
        std::map > 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;
    }
    

    Shortcomings:

    1. Storage structure is read-only. This is a necessary shortcoming, because the index will become outdated if you modify the indexed fields of an object while it's in the datastore. To modify an object, remove it and then re-add another one.
    2. All fields must be unique. This is easily changeable, but you need to keep maps containing list as indices for non-unique fields, not just maps containing Thing.
    3. Performance problems relating to using std::map. std::unordered_map is an alternative with better (constant amortized) access times for huge data structures (the same as std::unordered_set).

    Deviations:

    1. Given that you have a clear key-value relationship here, I think you'd be better off with a map than a set.
    2. In order to solve performance problems relating to reference counting, if you are careful to maintain the internal consistency at all times, you could forgo all smart pointers for raw ones, and return values via references, and you could achieve further improvements by using unsafe object ownership semantics when filling it (i.e., pass it pointers to heap objects that the Datastore then takes ownership of). More complex, but ultimately fewer copies and less runtime overhead.

提交回复
热议问题