When classes want couples

I have a problem with two classes that were once nicely separated, but now they want to put together.

Without delving into the specifics of the problem, here it is:

I had a Triangle class that contained 3 vertices of spatial position.

class Triangle { Vertex a,b,c ; // vertices a, b and c } ; 

There were many instances of Triangle in the program, so each of them saved its own copy of its vertices. Member functions, such as getArea() , getCentroid() , etc., were written in the Triangle class, and since each Triangle instance had copies of the vertices a, b, and c, the search for a region or centroid was independent of other classes. As it should be!

Then I wanted to go into the vertex array / index buffer style representation for other reasons. This means that all vertices are stored in one array located in the Scene object, and each Triangle stores only the LINKS of the vertices in the Scene , and not copies of the vertices themselves. At first I tried disabling pointers:

 class Scene { std::vector<Vertex> masterVertexList ; } ; class Triangle { Vertex *a,*b,*c ; // vertices a, b and c are pointers // into the Scene object master vertex list } ; 

(If you are wondering about the benefits, I did this for reasons mainly related to the triangles that separate the vertices. If * a moves, all the triangles that use this vertex are updated automatically).

That would be a really good solution! But it did not work reliably because std :: vector invalidates pointers , and I used std :: vector to list the main vertices in the Scene class.

So I had to use integers:

 class Triangle { int a,b,c ; // integer index values // into the Scene object master vertex list } ; 

But now I have this new communication problem: in order to find its own area or center, the Triangle class needs access to the class Scene , where it had not been before. It seems that I have something, but really.

WWYD?

+6
c ++ class
source share
7 answers

Why doesn't vector just need to store pointers in Scene ?

 std::vector<Vertex *> masterVertexList; 

That way, Triangle can continue to use Vertex * , and all you have to do is make sure the pointers are removed in the Scene destructor.

+4
source share

You can pass a vector to a triangle in its constructor so that it can store a link to the vector. Then he does not need to have access or to know about the Stage.

 typedef std::vector<Vertex> VertexContainer; class Scene { VertexContainer masterVertexList ; } ; class Triangle { // A references to the vertices contained in Scene. // A triangle no longer needs to know anything about a scene VertexContainer& vertexListRef; // index into vertexListRef of the triangles points. VertexContainer::size_type a; VertexContainer::size_type b; VertexContainer::size_type c; public: Triangle(VertexContainer& masterVertexList, VertexContainer::size_type x, VertexContainer::size_type y, VertexContainer::size_type z) :vertexListRef(masterVertexList) ,a(x),b(y),c(z) {} }; 
+4
source share

It seems to me that your Triangle really depends on your scene (since its vertices are members of this particular scene), so there is no shame in making an object. In fact, I would probably provide Triangle as a required member of Scene *.

+3
source share

Moving from unconnected to connected is a natural result of your decision to share vertices where possible. Previously, each triangle "owned" its vertices, and the scene (presumably) belonged to a bunch or triangles.

By allowing triangles to share vertices, the basic model changes - when / if a vertex can be divided between two or more triangles, no triangle can own this vertex. Although it is possible (for example, with something like shared_ptr) to have a distributed sharing scheme, what you are doing right now is probably more straightforward: each vertex still has one owner, but now the scene is owned by an individual triangle instead of an individual triangle.

Since now a triangle is only a convenient way of grouping some vertices in a “owning” collection instead of owning the vertices themselves, it is not surprising that there is a more rigid connection between the triangle and the collection that owns its vertices. If you enjoyed it a lot, you can still hide your co-ownership to preserve at least the look of your previous weakening connection.

The general idea would be quite simple: instead of each triangle, knowing the scene that contains the vertices of the triangle, you must create a proxy vertex class that combines the scene identifier and the vertex index, so the triangle can manipulate the vertex proxy server, as before, would have vertex object. You do not completely eliminate the stiffer grip, but you isolate “knowledge” from a stricter bond with one class, which is only responsible for maintaining a weaker grip.

An obvious drawback to this would be that vertex objects-objects are likely to store enough redundant data. For example, all proxy vertices in any given triangle clearly represent vertices in the same scene. If you store the scene ID explicitly for each proxy, you save three copies of the scene ID, not the one you had before. Sometimes it's worth it; others are not. If this is a real problem, you can try to find a way to avoid storing the scene identifier explicitly, but this will probably be due to some tricks that are not (even close) language agnostics.

+1
source share

If you add or remove only to the end of the vertex list, use deque instead.

+1
source share

I do not think this is too bad. Triangle lost some generality and became the Scene peripheral class, but if it is not used as an external interface (and this binding to internal buffers does not imply), this is just a natural evolution.

My solution will be similar to yours under the hood, but with lots of sugar.

 struct Triangle { Triangle( ... ) { ... } Vertex *a(),*b(),*c() ; // trivia: this is valid syntax! Getters adjust… private: size_t ax, bx, cx; // … offsets… Scene *client; // … into the Scene object master vertex list. } ; 

This way you don’t need to reorganize things in memory, and to adapt the old code you just need to add () to ->a and .a etc., which can be done by searching and replacing, and improves the OO style.

Or, do away with the constructor and private and make it a POD.

+1
source share

Assuming you have only one Scene , you can make it a single object and access the list of vertices using static methods.

If you have several Scene objects, then each Triangle belongs to exactly one Scene - and it must "know" which scenario it belongs to. Therefore, you must initialize each triangle with a Scene reference and store it as a member of the class.

0
source share

All Articles