Dans mon projet C ++, j'ai deux classes, Particle
et Contact
. Dans la Particle
classe, j'ai une variable membre std::vector<Contact> contacts
qui contient tous les contacts d'un Particle
objet, et les fonctions membres correspondantes getContacts()
et addContact(Contact cont)
. Ainsi, dans "Particle.h", j'inclus "Contact.h".
Dans la Contact
classe, je voudrais ajouter du code au constructeur pour Contact
cet appel Particle::addContact(Contact cont)
, afin qu'il contacts
soit mis à jour pour les deux Particle
objets entre lesquels l' Contact
objet est ajouté. Ainsi, je devrais inclure "Particle.h" dans "Contact.cpp".
Ma question est de savoir si cela est acceptable / bonne pratique de codage et, sinon, quelle serait une meilleure façon de mettre en œuvre ce que j'essaie d'atteindre (simplement mettre à jour automatiquement la liste des contacts pour une particule spécifique chaque fois qu'un nouveau contact est créé).
Ces classes seront liées entre elles par une Network
classe qui aura N particules ( std::vector<Particle> particles
) et Nc contacts ( std::vector<Contact> contacts
). Mais je voulais pouvoir avoir des fonctions comme particles[0].getContacts()
- est-ce correct d'avoir de telles fonctions dans la Particle
classe dans ce cas, ou y a-t-il une meilleure "structure" d'association en C ++ à cet effet (de deux classes liées utilisées dans une autre classe) .
J'ai peut-être besoin d'un changement de perspective ici dans la façon dont j'aborde cette question. Étant donné que les deux classes sont connectées par un Network
objet de classe, est-ce une organisation de code / classe typique d'avoir des informations de connectivité entièrement contrôlées par l' Network
objet (en ce qu'un objet Particle ne devrait pas être au courant de ses contacts et, par conséquent, il ne devrait pas avoir de getContacts()
membre une fonction). Ensuite, afin de savoir quels contacts a une particule spécifique, j'aurais besoin d'obtenir ces informations via l' Network
objet (par exemple, en utilisant network.getContacts(Particle particle)
).
Serait-il moins typique (peut-être même découragé) de concevoir une classe C ++ pour un objet Particle d'avoir également ces connaissances (c'est-à-dire, avoir plusieurs façons d'accéder à ces informations - via l'objet Network ou l'objet Particle, selon ce qui semble le plus pratique) )?
la source
Network
objet de classe qui contient desParticle
objets et desContact
objets. Avec cette connaissance de base, je peux ensuite essayer d'évaluer si elle correspond ou non à mes besoins spécifiques, qui sont toujours explorés / développés au fur et à mesure de l'avancement du projet.Réponses:
Votre question comporte deux parties.
La première partie est l'organisation des fichiers d'en-tête C ++ et des fichiers source. Ceci est résolu en utilisant la déclaration directe et la séparation de la déclaration de classe (en les plaçant dans le fichier d'en-tête) et du corps de la méthode (en les plaçant dans le fichier source). De plus, dans certains cas, on peut appliquer l' idiome Pimpl ("pointeur vers l'implémentation") pour résoudre des cas plus difficiles. Utilisez des pointeurs de propriété partagée (
shared_ptr
), des pointeurs de propriété unique (unique_ptr
) et des pointeurs non propriétaires (pointeur brut, c'est-à-dire «astérisque») conformément aux meilleures pratiques.La deuxième partie est de savoir comment modéliser des objets qui sont interdépendants sous la forme d'un graphique . Les graphiques généraux qui ne sont pas des DAG (graphiques acycliques dirigés) n'ont pas un moyen naturel d'exprimer la propriété arborescente. Au lieu de cela, les nœuds et les connexions sont tous des métadonnées qui appartiennent à un seul objet graphique. Dans ce cas, il n'est pas possible de modéliser la relation nœud-connexion sous forme d'agrégations. Les nœuds ne "possèdent" pas de connexions; les connexions ne "possèdent" pas de nœuds. Au lieu de cela, ce sont des associations, et les nœuds et les connexions appartiennent au graphique. Le graphique fournit des méthodes de requête et de manipulation qui opèrent sur les nœuds et les connexions.
la source
particles[0].getContacts()
- suggérez-vous dans votre dernier paragraphe que je ne devrais pas avoir de telles fonctions dans laParticle
classe, ou que la structure actuelle est correcte parce qu'elles sont intrinsèquement liées / associéesNetwork
? Y a-t-il une meilleure "structure" d'association en C ++ dans ce cas?network.particle[p]
aurait un correspondantnetwork.contacts[p]
avec les indices de ses contacts. Sinon, le réseau et la particule suivent tous les deux les mêmes informations.Particle
objet ne devrait pas être au courant de ses contacts (donc je ne devrais pas avoir degetContacts()
fonction membre), et que ces informations ne devraient provenir que de l'Network
objet? Serait-ce une mauvaise conception de classe C ++ pour unParticle
objet d'avoir cette connaissance (c'est-à-dire, avoir plusieurs façons d'accéder à cette information - via l'Network
objet ou l'Particle
objet, selon ce qui semble le plus pratique)? Ce dernier semble avoir plus de sens pour moi, mais peut-être que je dois changer ma perspective à ce sujet.Particle
tout savoir surContact
s ouNetwork
s est qu'il vous lie à une manière spécifique de représenter cette relation. Les trois classes devront peut-être s'entendre. Si au lieu de cela,Network
c'est le seul qui sait ou se soucie, c'est seulement une classe qui doit changer si vous décidez qu'une autre représentation est meilleure.Particle
etContact
doivent être complètement séparés, et l'association entre eux est définie par l'Network
objet. Juste pour être complètement sûr, c'est (probablement) ce que @rwong voulait dire quand il / elle a écrit, "les nœuds et les connexions sont" possédés "par le graphique. Le graphique fournit des méthodes de requête et de manipulation qui opèrent sur les nœuds et les connexions." , droite?Si je vous comprends bien, le même objet de contact appartient à plus d'un objet de particules, car il représente une sorte de contact physique entre deux ou plusieurs particules, non?
La première chose que je pense est sujette à caution est pourquoi
Particle
a une variable membrestd::vector<Contact>
? Ce devrait être unstd::vector<Contact*>
ou un à lastd::vector<std::shared_ptr<Contact> >
place.addContact
devrait alors avoir une signature différente commeaddContact(Contact *cont)
ou à laaddContact(std::shared_ptr<Contact> cont)
place.Il n'est donc pas nécessaire d'inclure "Contact.h" dans "Particle.h", une déclaration directe de
class Contact
dans "Particle.h" et une inclusion de "Contact.h" dans "Particle.cpp" seront suffisantes.Ensuite, la question sur le constructeur. Tu veux quelque chose comme
Droite? Cette conception est correcte, tant que votre programme connaît toujours les particules associées au moment où un objet contact doit être créé.
Remarquez que si vous
std::vector<Contact*>
route, vous devez réfléchir à la durée de vie et à la propriété desContact
objets. Aucune particule ne "possède" ses contacts, un contact ne devra probablement être supprimé que si les deuxParticle
objets associés sont détruits. Utiliser à lastd::shared_ptr<Contact>
place résoudra automatiquement ce problème. Ou vous laissez un objet "contexte environnant" s'approprier les particules et les contacts (comme suggéré par @rwong) et gérer leur durée de vie.la source
addContact(const std::shared_ptr<Contact> &cont)
plusaddContact(std::shared_ptr<Contact> cont)
?move
paradigmeparticle1.getContacts()
et departicle2.getContacts()
livrer le mêmeContact
objet représentant le contact physique entreparticle1
etparticle2
, et non deux objets différents. Bien sûr, on pourrait essayer de concevoir le système d'une manière peu importe s'il y a deuxContact
objets disponibles en même temps représentant le même contact physique. Cela impliquerait de rendreContact
immuable, mais êtes-vous sûr que c'est ce que vous voulez?Oui, ce que vous décrivez est un moyen très acceptable de garantir que chaque
Contact
instance figure dans la liste des contacts de aParticle
.la source
Ce que vous avez fait est correct.
Une autre façon ... Si le but est de s'assurer que tout
Contact
est dans une liste, alors vous pourriez:Contact
(constructeurs privés),Particle
classe,Particle
classe un ami deContact
,Particle
créer une méthode d'usine qui crée unContact
Ensuite, vous n'avez pas à inclure
particle.h
danscontact
la source
Network
classe, cela change-t-il la structure suggérée, ou serait-ce toujours la même chose?Une autre option que vous pourriez envisager est de faire du constructeur Contact qui accepte une référence de particule un modèle. Cela permettra à un contact de s'ajouter à n'importe quel conteneur qui implémente
addContact(Contact)
.la source