Implementing many-to-many relationships
-
@kshegunov said in Implementing many-to-many relationships:
You can't not use pointers,
Please look at the code sample I provided various comments back.
It also links to some kde git repo which shows this in practice.
Yes, you can absolutely code "without" pointers. Objects can live on stack, Qt does that in many situations by default for many objects.
You can have a QList of QStrings. No pointers in your code at all. Same idea.
-
Hey guys - while I (truly) appreciate the points being made, what I was really looking for in my OP was opinions on sensible approaches to architecting this application. I'm more concerned with readability and maintainability than memory leaks.
It appears that, apart from @SGaist 's original response, most people don't like the idea of intermediate tables, instead managing the relationships from within the respective objects...correct?
-
@mzimmers said in Implementing many-to-many relationships:
Hey guys - while I (truly) appreciate the points being made, what I was really looking for in my OP was opinions on sensible approaches to architecting this application. I'm more concerned with readability and maintainability than memory leaks.
Well, they aren't exclusive and I'd rather make the argument that you shouldn't have leaks at all.
It appears that, apart from @SGaist 's original response, most people don't like the idea of intermediate tables, instead managing the relationships from within the respective objects...correct?
I can't speak for everyone, but I certainly don't. I'd rather architect such an application in the usual OO way, where each item is responsible for whatever it is responsible, than to have the god object that knows everything about everybody.
@TomZ said in Implementing many-to-many relationships:
Yes, you can absolutely code "without" pointers. Objects can live on stack, Qt does that in many situations by default for many objects.
Yes, but sometimes, not always. Qt extensively uses owning and non-owning (weak) pointers to
QObject
s and that's perfectly fine. It will depend on the semantics, not the syntax (which is what a pointer is - syntax).You can have a QList of QStrings. No pointers in your code at all. Same idea.
When it's a data class, and that's only because Qt already wraps the pointer inside an object to hide that detail from you and give you value semantics. This doesn't mean this is applicable to everything, or rather that you should use it for everything. Not everything is a value, so not everything can behave this way.
-
@mzimmers said in Implementing many-to-many relationships:
opinions on sensible approaches to architecting this application.
I would architect it the way that I suggested. A bunch of structs that get stored in maps.
Relations between those objects happens via ints that are the 'keys' in those maps.Extremely easy to architect. It avoids memory overhead, avoids pointers and avoids the need to have a manager separate from your data classes as the manager IS your datastructure.
The "make it a nice API" is what I linked to the existing example which does exactly that in:
@TomZ said in Implementing many-to-many relationships:
postscript; if you must, you can make all of this the private content of a 'manager' class and provide some helper classes on top to make it look all object-oriented again. (example: manager, pretty-object, implementation).
I indeed hope we can get past the point where some don't understand it and say its a stupid idea. That's indeed not productive.
-
@TomZ said in Implementing many-to-many relationships:
avoids pointers
Umm, you mean it swaps pointers to memory for integer indexes into a map/array. No guarantee they are still valid/still have the same key as when they were created. Maybe less likely to "crash", more likely to refer to valid but incorrect information?
-
@JonB said in Implementing many-to-many relationships:
Umm, you mean it swaps pointers to memory for integer indexes into a map/array.
well, yes, but no.
You also skip doing 'new' and 'delete'.
You also have all data structures in one place and thus avoid any stupid things like 'friend classes' (or large amount of accessors which have the effect of making properties public).
It just keeps stuff simple.
-
@TomZ said in Implementing many-to-many relationships:
You also skip doing 'new' and 'delete'.
Doesn't seem to me that I'd need new/delete anyway, but I do agree that it seems preferable to:
- create and maintain a map of Equipment objects (or structs) with a UUID as the key
- in the Activity object, maintain a list of Equipment UUIDs.
Is this what you had in mind?
-
@mzimmers said in Implementing many-to-many relationships:
Doesn't seem to me that I'd need new/delete anyway, but I do agree that it seems preferable to:
create and maintain a map of Equipment objects (or structs) with a UUID as the key
in the Activity object, maintain a list of Equipment UUIDs.Is this what you had in mind?
The various comments here went towards using pointers to instances of classes as the relationship. Can't use a raw pointer (safely) without 'new'.
If your only objects are the Equipment and the Activity, then yeah, that's pretty simple.
as this got confusing, here is a very quick and dirty mockup I just wrote in 5 minutes. Apologies for the roughness.
struct PActivity; struct PEquipment; class ManagerPrivate { public: std::map<int, PActivity> m_activities; std::map<int, PEquipment> m_equipments; int m_lastAssignedId = 0; }; struct PActivity { QString name; }; struct PEquipment { QString name; std::deque<int> activities; }: class Manager { public: ManagerPrivate *d; Activity createActivity(); }; // ---- class Activity { public: explicit Activity(Manager *parent, int n); Activity(); // gives invalid instance. bool isValid() const { return d && n; } QString name() const { assert(isValid()); auto i = d->m_activities.find(n); assert(i != d->m_activities.end()); return i->name; } void setName(const QString &name) { assert(isValid()); auto i = d->m_activities.find(n); assert(i != d->m_activities.end()); i->name = name; } int id() const { return n; } private: ManagerPrivate *d = nullptr; int n = 0; }; class Equipment { explicit explicit(Manager *parent, int n); explicit(); // gives invalid instance. bool isValid() const; QString name() const; void setName(const QString &name); void addActivity(const Activity &a) { assert(a.isValid()); assert(isValid()); auto i = d->m_equipments.find(n); assert(i != d->m_equipments.end()); i->activities.push_back(a->id()); } private: ManagerPrivate *d = nullptr; int n = 0; }
-