In meinem C ++ - Projekt habe ich zwei Klassen Particle
und Contact
. In der Particle
Klasse habe ich eine Mitgliedsvariable, std::vector<Contact> contacts
die alle Kontakte eines Particle
Objekts sowie die entsprechenden Mitgliedsfunktionen getContacts()
und enthält addContact(Contact cont)
. Daher füge ich in "Partikel.h" "Kontakt.h" ein.
In der Contact
Klasse möchte ich dem Konstruktor Code für Contact
diesen Aufruf hinzufügen Particle::addContact(Contact cont)
, damit dieser contacts
für beide Particle
Objekte aktualisiert wird , zwischen denen das Contact
Objekt hinzugefügt wird. Daher müsste ich "Particle.h" in "Contact.cpp" aufnehmen.
Meine Frage ist, ob dies eine akzeptable / gute Codierungspraxis ist oder nicht, und wenn nicht, was wäre ein besserer Weg, um das zu implementieren, was ich erreichen möchte (einfach ausgedrückt, die Liste der Kontakte für ein bestimmtes Partikel wird bei jedem neuen Kontakt automatisch aktualisiert geschaffen).
Diese Klassen werden durch eine Network
Klasse mit N Partikeln ( std::vector<Particle> particles
) und Nc Kontakten ( std::vector<Contact> contacts
) miteinander verbunden. Aber ich wollte in der Lage sein, Funktionen zu haben wie particles[0].getContacts()
- ist es Particle
in diesem Fall in Ordnung, solche Funktionen in der Klasse zu haben , oder gibt es in C ++ zu diesem Zweck eine bessere Assoziationsstruktur (von zwei verwandten Klassen, die in einer anderen Klasse verwendet werden)? .
Möglicherweise brauche ich hier einen Perspektivwechsel in meiner Herangehensweise. Da die beiden Klassen durch ein Network
Klassenobjekt verbunden sind, ist es eine typische Code- / Klassenorganisation, Konnektivitätsinformationen vollständig vom Network
Objekt steuern zu lassen (insofern sollte ein Partikelobjekt seine Kontakte nicht kennen und folglich kein getContacts()
Mitglied haben Funktion). Um dann zu wissen, welche Kontakte ein bestimmtes Teilchen hat, müsste ich diese Informationen über das Network
Objekt erhalten (z network.getContacts(Particle particle)
. B. mit ).
Wäre es weniger typisch (vielleicht sogar entmutigt), ein C ++ - Klassendesign für ein Partikelobjekt zu haben, dieses Wissen ebenfalls zu haben (dh mehrere Möglichkeiten zu haben, auf diese Informationen zuzugreifen - entweder über das Netzwerkobjekt oder das Partikelobjekt, je nachdem, was bequemer erscheint )?
quelle
Network
Klassenobjekt haben, dasParticle
Objekte undContact
Objekte enthält . Mit diesem Basiswissen kann ich dann versuchen zu beurteilen, ob es meinen spezifischen Bedürfnissen entspricht oder nicht, die im Verlauf des Projekts noch erforscht / entwickelt werden.Antworten:
Ihre Frage besteht aus zwei Teilen.
Der erste Teil ist die Organisation von C ++ - Header- und Quelldateien. Dies wird gelöst, indem die Vorwärtsdeklaration und die Trennung der Klassendeklaration (Einfügen in die Header-Datei) und des Methodenkörpers (Einfügen in die Quelldatei) verwendet werden. Darüber hinaus kann man in einigen Fällen das Pimpl-Idiom ("Zeiger auf die Implementierung") anwenden , um schwierigere Fälle zu lösen. Verwenden Sie Zeiger mit gemeinsamem Besitz (
shared_ptr
), Zeiger mit einzelnem Besitz (unique_ptr
) und Zeiger ohne Besitz (Rohzeiger, dh das "Sternchen") gemäß den Best Practices.Im zweiten Teil werden Objekte modelliert, die in Form eines Diagramms miteinander verknüpft sind . Allgemeine Diagramme, die keine DAGs sind (gerichtete azyklische Diagramme), haben keine natürliche Art, baumartigen Besitz auszudrücken. Stattdessen sind die Knoten und Verbindungen alle Metadaten, die zu einem einzelnen Diagrammobjekt gehören. In diesem Fall ist es nicht möglich, die Knoten-Verbindungs-Beziehung als Aggregationen zu modellieren. Knoten "besitzen" keine Verbindungen; Verbindungen "besitzen" keine Knoten. Stattdessen handelt es sich um Zuordnungen, und sowohl Knoten als auch Verbindungen gehören dem Diagramm. Das Diagramm enthält Abfrage- und Manipulationsmethoden, die auf den Knoten und Verbindungen ausgeführt werden.
quelle
particles[0].getContacts()
- schlagen Sie in Ihrem letzten Absatz vor, dass ich solche Funktionen nicht in derParticle
Klasse haben sollte oder dass die aktuelle Struktur in Ordnung ist, weil sie von Natur aus miteinander verbunden sindNetwork
? Gibt es in diesem Fall eine bessere Assoziationsstruktur in C ++?network.particle[p]
eine Übereinstimmungnetwork.contacts[p]
mit den Indizes seiner Kontakte. Ansonsten verfolgen sowohl das Netzwerk als auch das Partikel die gleichen Informationen.Particle
Objekt seine Kontakte nicht kennen sollte (also sollte ich keinegetContacts()
Mitgliedsfunktion haben) und dass diese Informationen nur aus demNetwork
Objekt stammen sollten? Wäre es ein schlechtes C ++ - Klassendesign für einParticle
Objekt, über dieses Wissen zu verfügen (dh mehrere Möglichkeiten zu haben, auf diese Informationen zuzugreifen - entweder über dasNetwork
Objekt oder dasParticle
Objekt, je nachdem, was bequemer erscheint)? Letzteres scheint mir sinnvoller zu sein, aber vielleicht muss ich meine Sichtweise dazu ändern.Particle
Wissen überContact
s oderNetwork
s besteht darin, dass Sie an eine bestimmte Art der Darstellung dieser Beziehung gebunden sind . Möglicherweise müssen alle drei Klassen übereinstimmen. Wenn stattdessenNetwork
nur der eine weiß oder sich darum kümmert, muss sich nur eine Klasse ändern, wenn Sie entscheiden, dass eine andere Darstellung besser ist.Particle
undContact
sollte völlig getrennt sein, und die Assoziation zwischen ihnen wird durch dasNetwork
Objekt definiert . Um ganz sicher zu sein, meinte @rwong (wahrscheinlich), als er / sie schrieb: "Sowohl Knoten als auch Verbindungen gehören dem Diagramm." Das Diagramm bietet Abfrage- und Manipulationsmethoden, die für die Knoten und Verbindungen gelten. " , Recht?Wenn ich Sie richtig verstanden habe, gehört dasselbe Kontaktobjekt zu mehr als einem Partikelobjekt, da es eine Art physischen Kontakt zwischen zwei oder mehr Partikeln darstellt, oder?
Das erste, was ich für fragwürdig halte, ist, warum
Particle
es eine Mitgliedsvariable gibtstd::vector<Contact>
. Es sollte stattdessen einstd::vector<Contact*>
oder ein seinstd::vector<std::shared_ptr<Contact> >
.addContact
dann sollte eine andere Signatur wieaddContact(Contact *cont)
oderaddContact(std::shared_ptr<Contact> cont)
stattdessen haben.Dies macht es unnötig, "Contact.h" in "Particle.h" aufzunehmen, eine Vorwärtsdeklaration von
class Contact
"Particle.h" und ein Include von "Contact.h" in "Particle.cpp" sind ausreichend.Dann die Frage nach dem Konstruktor. Du willst so etwas wie
Recht? Dieses Design ist in Ordnung, solange Ihr Programm die zugehörigen Partikel immer zu dem Zeitpunkt kennt, zu dem ein Kontaktobjekt erstellt werden muss.
Beachten Sie, dass Sie, wenn Sie den
std::vector<Contact*>
Weg gehen , einige Gedanken über die Lebensdauer und den Besitz derContact
Objekte investieren müssen . Kein Partikel "besitzt" seine Kontakte, ein Kontakt muss wahrscheinlich nur gelöscht werden, wenn beide verwandtenParticle
Objekte zerstört sind. Wenn Siestd::shared_ptr<Contact>
stattdessen verwenden, wird dieses Problem automatisch für Sie gelöst. Oder Sie lassen ein "umgebenden Kontext" -Objekt das Eigentum an Partikeln und Kontakten übernehmen (wie von @rwong vorgeschlagen) und deren Lebensdauer verwalten.quelle
addContact(const std::shared_ptr<Contact> &cont)
über nichtaddContact(std::shared_ptr<Contact> cont)
?move
Paradigmaparticle1.getContacts()
und zuparticle2.getContacts()
liefern, dasContact
den physischen Kontakt zwischenparticle1
und darstelltparticle2
, und nicht zwei verschiedene Objekte. Natürlich könnte man versuchen, das System so zu gestalten, dass es keine Rolle spielt, ob zweiContact
Objekte gleichzeitig verfügbar sind, die denselben physischen Kontakt darstellen. Dies würde bedeuten,Contact
unveränderlich zu machen , aber sind Sie sicher, dass dies das ist, was Sie wollen?Ja, was Sie beschreiben, ist eine sehr akzeptable Methode, um sicherzustellen, dass jede
Contact
Instanz in der Kontaktliste von a enthalten istParticle
.quelle
Was Sie getan haben, ist richtig.
Ein anderer Weg ... Wenn das Ziel darin besteht, sicherzustellen, dass jeder
Contact
in einer Liste enthalten ist, können Sie:Contact
(privaten Konstruktoren),Particle
Klasse,Particle
Klasse zu einem Freund vonContact
,Particle
erstellen Sie eine Factory-Methode, die eine erstelltContact
Dann müssen Sie nicht enthalten
particle.h
incontact
quelle
Network
ob sich mit meiner Bearbeitung der ersten Frage zur Klasse die vorgeschlagene Struktur ändert oder ob sie immer noch dieselbe ist.Eine weitere Option, die Sie in Betracht ziehen könnten, besteht darin, den Kontaktkonstruktor so zu gestalten, dass eine Vorlage mit Partikelreferenz akzeptiert wird. Auf diese Weise kann sich ein Kontakt zu jedem Container hinzufügen, der implementiert wird
addContact(Contact)
.quelle