In my C++ application, I have Pod objects that internally stores information about Monkeys in a vector. MonkeyInternal is a big class with a lot of properties about monkeys…
My intention here is to create a lightweight proxy object Monkey that merely stores an index that can be used to access more information stored in a private vector in Pod (Monkeys are capricious, and their properties may change, and we want any monkey objects in the program to always be up to date.).
class Pod {
public:
typedef Monkey monkey_type;
Monkey monkey(int monkey_index) {
return Monkey(this, monkey_index);
}
// Lightweight proxy Monkey object that can be passed around easily.
class Monkey {
public:
Monkey(Pod* pod, int monkey_index) {
pod_ = pod;
monkey_index_ = monkey_index;
}
private:
Pod* pod_;
int monkey_index_; // To get monkey internal info, index into pod.
friend class Pod; // Monkey is a proxy that delegates to Pod.
}
private:
// MonkeyInternal objects stores hefty data about monkeys.
vector<MonkeyInternal> monkeys_;
}
With these data structures, I can randomly access a monkey’s internal information easily. I can also easily insert another monkey into the pod efficiently (O(1) amortized).
However, removing a monkey is O(n) since I must shift all the monkeys to the left. I could make removal slightly more efficient by using an unordered_map (or some good hash table) – O(n), but is there any way I could organize my data structures to support O(1) removal of a monkey?
Perhaps I could use a clever design pattern?
Your Monkey class is effectively a smart pointer — it might be nice to make it behave like one syntactically as well so that users have the right mental model for what they’re dealing with.
As for the removal, I assume that you’re removing the MonkeyInternal that is stored by value in the vector? If the copying a MonkeyInternal is expensive then the best way to save is to store something cheap in the vector, like a MonkeyInternal* instead.
Once you go down this route you may well find it better to replace your Monkey type with something like std::shared_ptr and just be done with it.
BTW, so long as the copy is cheap, linear memory access is pretty cheap as the CPU memory prediction can work out what’s going on really well. This falls apart if you have to chase pointers so you’ll probably find that std::vector is faster than std::list even though it is technically O(n) rather than O(1) for insert and removal.