This question is about owning pointers, consuming pointers, smart pointers, vectors, and allocators.
I am a little bit lost on my thoughts about code architecture. Furthermore, if this question has already an answer somewhere, 1. sorry, but I haven't found a satisfying answer so far and 2. please point me to it.
My problem is the following:
I have several "things" stored in a vector and several "consumers" of those "things". So, my first try was like follows:
std::vector<thing> i_am_the_owner_of_things;
thing* get_thing_for_consumer() {
// some thing-selection logic
return &i_am_the_owner_of_things[5]; // 5 is just an example
}
...
// somewhere else in the code:
class consumer {
consumer() {
m_thing = get_thing_for_consumer();
}
thing* m_thing;
};
In my application, this would be safe because the "things" outlive the "consumers" in any case. However, more "things" can be added during runtime and that can become a problem because if the std::vector<thing> i_am_the_owner_of_things;
gets reallocated, all the thing* m_thing
pointers become invalid.
A fix to this scenario would be to store unique pointers to "things" instead of "things" directly, i.e. like follows:
std::vector<std::unique_ptr<thing>> i_am_the_owner_of_things;
thing* get_thing_for_consumer() {
// some thing-selection logic
return i_am_the_owner_of_things[5].get(); // 5 is just an example
}
...
// somewhere else in the code:
class consumer {
consumer() {
m_thing = get_thing_for_consumer();
}
thing* m_thing;
};
The downside here is that memory coherency between "things" is lost. Can this memory coherency be re-established by using custom allocators somehow? I am thinking of something like an allocator which would always allocate memory for, e.g., 10 elements at a time and whenever required, adds more 10-elements-sized chunks of memory.
Example:
initially:
v = ☐☐☐☐☐☐☐☐☐☐
more elements:
v = ☐☐☐☐☐☐☐☐☐☐ 🡒 ☐☐☐☐☐☐☐☐☐☐
and again:
v = ☐☐☐☐☐☐☐☐☐☐ 🡒 ☐☐☐☐☐☐☐☐☐☐ 🡒 ☐☐☐☐☐☐☐☐☐☐
Using such an allocator, I wouldn't even have to use std::unique_ptr
s of "things" because at std::vector
's reallocation time, the memory addresses of the already existing elements would not change.
As alternative, I can only think of referencing the "thing" in "consumer" via a std::shared_ptr<thing> m_thing
, as opposed to the current thing* m_thing
but that seems like the worst approach to me, because a "thing" shall not own a "consumer" and with shared pointers I would create shared ownership.
So, is the allocator-approach a good one? And if so, how can it be done? Do I have to implement the allocator by myself or is there an existing one?
things
ahead? If yes the callreserve
on a vector and there will not be a reallocation of elements. – Dormanthing
and how it behaves. – Dormanm_thing
pointers must remain valid. – Medawarthing
is? Does it accept callbacks? Or is does it behave like a structural type? Do multiple consumers communicate usingthing
(do consumer changesthing
)? Does it contain other pointers? – Dormanthing
: Multiple consumers can change it, and it can even cointain other pointers. – Medawardo_something_on_thing(functor, consumer)
? This would call the function of a consumer directly on the thing in the vector instead of assigning a thing to a consumer. – Heteropolarthing
, the prudent approach is to use a vector of some kind of pointers. Allocate eachthing
independently, and pass the pointer to the consumers. The pointers may be plain pointers or, better,std::shared_ptr<>
s, because you effectively have shared ownership: You must not delete athing
as long as one of its consumers is still alive. – Utricleconsumer
needs to keep the pointer tothing
in order to check at some later point in time whether it needs to update itself based on the changes that happened tothing
. (I wanted to leave out these information because the question should be on point and not cluttered with unneccessary detail.) – Medawarstd::deque
. – Chkalov