I have a collection of Creature
objects that are created and owned in one part of my application using std::make_shared
and std::shared_ptr
.
I also keep track of a selection of zero or one Creature
in a World
object using a std::weak_ptr<Creature>
.
void World::SetSelection(const std::shared_ptr<Creature>& creature) {
selection = creature;
}
std::shared_ptr<Creature> World::GetSelection() const {
return selection.lock();
}
The caller of GetSelection
is responsible for checking if the pointer is empty. If it is, that means there is currently no selection.
This all works perfectly to my liking: when the selected Creature
dies of natural causes (elsewhere in the application), GetSelection
starts returning a nullptr
again as if nothing was ever selected.
However in that case the World::selection
member still points to the std::shared_ptr
's control block. This can be quite big because I use std::make_shared
to create my Creature
objects (I realize that the Creature
object was correctly destroyed at the right time but the memory for it is still allocated). I'm considering changing GetSelection
to this:
std::shared_ptr<Creature> World::GetSelection() {
const auto ret = selection.lock();
if (!ret)
selection.reset();
return ret;
}
This frees up the memory as soon as I notice it's not needed anymore. Annoyingly, this version of GetSelection
cannot be const
.
My questions:
Which version of
GetSelection
would be considered best practice in this situation?Does the answer change if something similar happens in templated code, where
sizeof(T)
is unknown and could be huge? Or in C++14 wherestd::make_shared<T[]>
could be involved?If the second version is always best, what is the rationale for
std::weak_ptr<T>::expired
andlock
not doing it themselves?