Using an std::vector of pointer objects. Is there a way to delete an item from the vector when it goes out of scope?

Viewed 225

I'm making the mental transition from old school C++ to "modern".

I have a class factory that is used to make sure all instances of a certain class, foo, are maintained in a list. This list is used to process all foos in order.

This is in a legacy system that uses regular old pointers (new/delete) but I was thinking of turning them into shared_ptrs.

Will I still need a "RemoveMeFromList()" call in the foo's destructor or is there some magic I'm not aware of?

4 Answers

What I propose you in this case is to keep a vector of weak pointers (std::weak_ptr) in your factory:

std::vector<std::weak_ptr<foo>> foo_instances;

Now, with this vector, if one pointer goes out of the scope, it will be expired. So, you can remove the expired pointers next time that you process the vector. In other words, you do not need an explicit "RemoveMeFromList" method, but you can do the removal of expired pointers when you go for processing of the foo instances.

Will I still need a "RemoveMeFromList" call in the foo's destructor

Yes.

or is there some magic I'm not aware of?

No.

You could implement a custom container, that stores weak pointers to objects in shared ownership, and on every access, check whether the pointer is still valid. I don't know if this would be a good design for you (seems dubious).

This is in a legacy system that uses regular old pointers (new/delete) but I was thinking of turning them into shared_ptrs.

You have not really provided us with a lot to go on, but I suspect std::shared_ptr is not what you need. std::shared_ptr is used to "collaboratively manage" ownership by all copies of the pointer, and kill it when there are none. Such killing will not notify your "factory", or central repository, of foos. But since you actually have it, and you want it to keep track of the instances, just let it be the one owning them. At most, you could have the entities which get pointers/references to foo instances instead get wrappers which handle just ref-counting somehow. Your central foo repository will handle deletions - if they are at all necessary. It could recycle unused foos - reinitialize them and hand them out instead of really allocating anything new.

Again, sorry for the rather vague and abstract answer.

You could implement a solution with shared_ptr and weak_ptr as eerorika suggested by doing something like this. You might prefer this solution to unique_ptr if you don't want the Factory to be the owner of the objects which determines when things are invalid.

Your factory class could look something like this:

class Factory {
   public:
      std::shared_ptr<MyObject> create();
      std::vector<std::weak_ptr<MyObject>>& getObjects();
   private:
      std::vector<std::weak_ptr<MyObject>> objects;
      void cullInvalidObjects();
};

We simply create a weak_ptr from the shared_ptr of the object in the factory method:

std::shared_ptr<MyObject> Factory::create()
{
   auto object = std::make_shared<MyObject>();
   std::weak_ptr<MyObject> weakObject = object;
   objects.push_back(weakObject);
   return object;
};

The factory can have an internal method that culls objects that are no longer valid:

void Factory::cullInvalidObjects()
{
    auto iter = objects.begin();
    while (iter != objects.end())
    {
        if ((*iter).expired())
        {
            objects.erase(iter++);
        }
    }
}

and then when getting objects to work on them, you would probably want to cull the list before providing it:

std::vector<std::weak_ptr<MyObject>>& Factory::getObjects()
{
   cullInvalidObjects()
   return objects;
}

Alternatively, rather than culling you could just get the list and then check for validity in your functions that process it. This is probably not the best solution if you have a large number of invalid items.

void callSomeMethod(std::vector<std::weak_ptr<MyObject>>& objects)
{
   for (auto wObj : objects)
   {
       auto sObj = wObj.lock();
       if(p)
       {
         p->someMethod();
       }
   }
}
Related