Here is the question’s plot: suppose I have some abstract classes for objects, let’s call it Object. It’s definition would include 2D position and dimensions. Let it also have some virtual void Render(Backend& backend) const = 0 method used for rendering.
Now I specialize my inheritance tree and add Rectangle and Ellipse class. Guess they won’t have their own properties, but they will have their own virtual void Render method. Let’s say I implemented these methods, so that Render for Rectangle actually draws some rectangle, and the same for ellipse.
Now, I add some object called Plane, which is defined as class Plane : public Rectangle and has a private member of std::vector<Object*> plane_objects;
Right after that I add a method to add some object to my plane.
And here comes the question. If I design this method as void AddObject(Object& object) I would face trouble like I won’t be able to call virtual functions, because I would have to do something like plane_objects.push_back(new Object(object)); and this should be push_back(new Rectangle(object)) for rectangles and new Circle(...) for circles.
If I implement this method as void AddObject(Object* object), it looks good, but then somewhere else this means making call like plane.AddObject(new Rectangle(params)); and this is generally a mess because then it’s not clear which part of my program should free the allocated memory.
[“when destroying the plane? why? are we sure that calls to AddObject were only done as AddObject(new something).]
I guess the problems caused by using the second approach could be solved using smart pointers, but I am sure there have to be something better.
Any ideas?
Your actual problem seems to be managing the objects’ lifetimes. Four possibilities that come to mind are:
Your container (i.e.
Plane) assumes ownership of all contained objects and thereforedeletes them once it’s itself destroyed.Your container (
Plane) does not assume ownership and whoever added objects to your container will be responsible for destroying them.The lifetime of your objects is managed automatically.
You circumvent the problem by providing the container with a clone of the actual object. The container manages a copy of the object, and the caller manages the original object.
What you currently have seems like approach #4. By doing:
you insert a copy of the object into the container. Therefore the problem sort of disappears. Ask yourself whether this is really what you want, or if one of the above choices would be more suitable.
Options #1 and #2 are easy to implement, because they define a contract that your implementation simply has to follow. Option #3 would call for e.g. smart pointers, or some other solution involving reference counting.
If you want to keep following the approach of option #4, you could e.g. extend your
Objectclass with aclonemethod, so that it returns the right type of object. This would get rid of the incorrectnew Object(...).P.S.: Note how the STL containers seem to deal with this issue: Let’s say you declare a
vector<Foo>. This vector will contain copies of the objects inserted into it (option #4 in my answer). However, if you declare the collection asvector<Foo*>, it will contain references to the original objects, but it will not manage their lifetime (option #2 in my answer).