I am currently manually managing the lifecycle of objects in my project. I am considering switching to smart pointers, specifically tr1::shared_pointer and tr1::weak_ptr. However, I am seeing a couple of problems and would like to get some input on best practices.
Consider the following class diagram:
In this diagram, the thick arrows represent associations with an ownership semantics (the source is responsible for deleting the target or targets). The thin arrows represent associations without ownership.
From what I understand, one way of implementing an association with ownership semantics would be to use tr1::shared_ptr (or a collection thereof). Other associations can be implemented using either tr1::shared_ptr or tr1::weak_ptr. The former is prohibited if it may result in circular references because that would prevent proper release of resources.
As you can see, there is a circle of associations between classes Edge and Side. I can easily break this by implementing the "left" and "right" associations from Edge to Side using tr1::weak_ptrs. However, I would prefer using the smart pointers to document, in code, the ownership semantics of the associations. So I would like to use shared_ptrs only for the associations represented by thick arrows in the diagram, and weak_ptrs for everything else.
Now my first question is: Should I use weak_ptrs liberally as described above, or should I use them as little as possible (only to avoid circular references)?
The next question is: Suppose I have a function that computes the average of a set of vertices. Suppose further that I have implemented the association from the Polyhedron to its Vertices like so:
class Vertex;
class Polyhedron {
protected:
std::vector<std::tr1::shared_ptr<Vertex> > m_vertices;
};
and that I have implemented the association from a Side to its Vertices like so:
class Vertex;
class Side {
protected:
std::vector<std::tr1::weak_ptr<Vertex> > m_vertices;
};
Note that the set of vertices of a Side is a subset of the set of vertices of a Polyhedron. Now let's say I have a function that computes the average of a set of vertices. The function is currently declared like so:
const Vertex centerOfVertices(std::vector<Vertex*> vertices);
Now if I represent the associations as above, I suddenly need two functions if I understand everything correctly:
const Vertex centerOfVertices(std::vector<std::tr1::shared_ptr<Vertex> > vertices);
const Vertex centerOfVertices(std::vector<std::tr1::weak_ptr<Vertex> > vertices);
Because I cannot convert between a vector of shared_ptr and a vector of weak_ptr. This smells funny to me. I would like to know what approach I should take here to avoid such a situation.