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
GetSelectionwould 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>::expiredandlocknot doing it themselves?