Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning Smart Pointers

Suppose I had a Manager Class that held a vector of some object:

class SomeObjectManager
{
private:
    std::vector<SomeObject> _heldObjects;
};

And in that class I had some function that iterated through said vector to return the requested object.

SomeObject getSomeObjectByName(std::string nameToFind);

What I need to know is when is it proper to use smart pointers. Should I actually be returning something like below?

std::shared_ptr<SomeObject> getSomeObjectByName(std::string nameToFind);

Or should I be using something else like unique_ptr or weak_ptr? I want the SomeObjectManager class to own the actual object being returned and never have said SomeObject be deleted unless the Manager makes it so.

I have only recently came back to the C++ world after being in C# mode for quite some time; thanks for the help and clearing up my confusion.

I have read a lot about this matter but never really found a straight answer to my particular situation.


Edit #1

I'd like to reword my last few sentences with this:

I want the SomeObjectManager class to own the actual object being returned and never have said SomeObject be removed from the vector and subsquently deleted, fall out of scope, until the Manager forces it to do so. For example:

void SomeObjectManager::removeSomeObjectByName(const std::string& objectToRemove);

This would just iterate over the vector, finding said SomeObject, and remove it from the Vector.

like image 818
Mister Avatar asked Dec 20 '22 15:12

Mister


1 Answers

Since SomeObjectManager is the owner of the SomeObject instances (stored in its std::vector data member), I'd just return raw pointers, since they are actually observing pointers.

std::vector<SomeObject> _heldObjects;

SomeObject* getSomeObjectByName(const std::string& nameToFind) {
    ... find index of object corresponding to 'nameToFind'
    return &_heldObjects[foundIndex];
}

(Note that I passed nameToFind using reference to const, since I assume that nameToFind is an input string, so if inside the method you are just observing that string, you can avoid deep-copies using const &).

You must pay attention when you have owning raw pointers (they should be wrapped inside safe RAII boundaries), but observing raw pointers are fine.

Just make sure that the lifetime of SomeObjectManager exceeds that of its clients, to make sure that the clients are referencing valid objects.

Note also that if you add new items to the vector data member (e.g. using std::vector::push_back()), the addresses of the previous SomeObject instances stored in the vector can change. So, if you gave pointers to those outside, they become invalid.

So, make sure that the vector size and vector content are not changed before you give pointers to its elements to client code outside.

An alternative would be to have std::vector<std::unique_ptr<SomeObject>> as data member. In this case, even if the vector is resized, the addresses you returned using the smart pointers (in particular using std::unique_ptr::get()) are still valid:

std::vector<std::unique_ptr<SomeObject>> _heldObjects;

SomeObject* getSomeObjectByName(const std::string& nameToFind) {
    ... find index of object corresponding to 'nameToFind'
    return _heldObjects[foundIndex].get();
}

PS
Another option might be returning references to const SomeObject (assuming that this use of const makes sense in your design):

std::vector<SomeObject> _heldObjects;

const SomeObject& getSomeObjectByName(const std::string& nameToFind) const {
    ... find index of object corresponding to 'nameToFind'
    return _heldObjects[foundIndex];
}
like image 50
Mr.C64 Avatar answered Jan 11 '23 22:01

Mr.C64