To use a "C++ Container of Pointers/References to Existing Objects", or pretty much anything else, you should understand the concept of ownership. Summary: ownership is responsibility for cleanup. You should think which entity should delete your objects. Often, you should rethink this whenever you change your code or add features to it.
std::vector<Object> objectList {obj1, obj2, obj3};
- here, objectList
contains copies of objects obj1
, obj2
and obj3
, and is responsible for deleting them (i.e. owns them). Whichever entity contained the original obj1
, obj2
and obj3
retains ownership of these (i.e. will delete them).
A wider context:
void do_stuff()
{
Object obj1;
Object obj2;
Object obj3;
std::vector<Object> objectList {obj1, obj2, obj3};
}
Here, the scope (or "stack frame") of function do_stuff
owns the three objects: they will be deleted when control goes out of the scope. This is a situation where something other than an object owns objects.
std::vector<Object*> objectList {&obj1, &obj2, &obj3};
- here, objectList
contains pointers to objects obj1
, obj2
and obj3
. The ownership of objects themselves is not changed - whatever owned them continues owning them. Some comment mentions "always recommend avoiding using raw pointers"; "always" is nonsense, but this is a good example for illustrating that recommendation.
A wider context:
std::vector<Object*> someObjectList;
void do_stuff()
{
Object obj1;
Object obj2;
Object obj3;
std::vector<Object*> objectList {&obj1, &obj2, &obj3};
someObjectList = objectList;
}
Here, the stack frame of function do_stuff
owns the three objects. If you have pointers to these objects, it's easy to "send" them to some other entity (someObjectList
), which will store the pointers even after the objects have been cleaned up. This is the reason why people "recommend avoiding using raw pointers"; but the real problem here is more fundamental - it's having a "Container of Pointers/References to Existing Objects". This is what people should recommend not doing.
If you really want to reference existing objects, you should make sure your ownership pattern supports this. You can do this in either of these ways:
Use an entity which owns the objects; make sure all lists of pointers to objects get deleted before the "owner of everything" is deleted. Often, the semantics of your application demand this anyway.
Use shared ownership. For that, you should apply shared ownership right at the creation point of your objects.
std::shared_ptr<Object> obj1 = std::make_shared(whatever1);
std::shared_ptr<Object> obj2 = std::make_shared(whatever2);
std::shared_ptr<Object> obj3 = std::make_shared(whatever3);
std::vector<std::shared_ptr<Object>> objectList(obj1, obj2, obj3);
You should think about a situation where the only "link" to the objects is your list of pointers. Does this make sense in your application? If yes, use shared ownership. If not, this is not a good idea, because it hides bugs.