Question

Toutes mes excuses si cela a déjà été demandé, je ne suis pas tout à fait sûr de la terminologie ou de la manière de poser la question.

Je me demande s'il existe des bibliothèques ou des pratiques recommandées pour implémenter des modèles d'objet en C ++. Si j'ai un ensemble de classes où les instances de ces classes peuvent avoir des relations entre elles et peuvent être accessibles les unes aux autres via différentes méthodes, je souhaite choisir un bon ensemble de structures de données sous-jacentes pour gérer ces instances et leurs relations réciproques. C’est facile en Java puisqu’il gère pour moi l’allocation de mémoire et la corbeille, mais en C ++, je dois le faire moi-même.

Le modèle d'objet de document de HTML est un exemple; comme autre exemple (artificiel), supposons que j'ai ces classes:

  • Entity
  • Person (sous-classe de Couple)
  • Property (sous-classe de House)
  • Pet
  • Car (sous-classe de home)
  • pets (sous-classe de cars)
  • children (sous-classe de spouse)

et ces relations:

  • marriage
    • a 1 parents de la classe members
    • a 0 ou plus owner de classe std::map
    • a 0 ou plus std::vector de classe <=>
    • a 0 ou plus <=> de classe <=>
  • <=>
    • a 0 ou 1 <=> de classe <=>
    • a 0 ou 1 <=> de classe <=>
    • a 0 ou 1 <=> de classe <=> (dans ce modèle, les parents n'existent pas s'ils ne sont pas en vie!)
  • <=>
    • a 2 <=> de la classe <=>
  • <=>
    • a 1 <=> de la classe <=>

Maintenant que j'ai réfléchi à ces objets et à leurs relations, je souhaite commencer à créer des structures de données, des méthodes et des champs pour les traiter, et voici où je me perds, car je dois gérer l'allocation de mémoire, la gestion de la durée de vie et tout ça. Vous pouvez rencontrer des problèmes tels que les suivants: Je pourrais vouloir mettre un objet dans un <=> ou un <=>, mais si je le fais, je ne peux pas stocker les pointeurs sur ces objets, car ils peuvent être déplacés lorsque la carte ou le vecteur augmente ou diminue.

Une des méthodes que j’ai beaucoup utilisées lorsque je travaillais avec COM consistait à avoir une collection cachée contenant tout. Chaque objet de la collection avait un identifiant unique (un numéro ou un nom), grâce auquel il pouvait être recherché dans la collection, et chaque objet avait un pointeur sur la collection. Ainsi, si vous avez un objet qui veut pointer vers un autre objet, au lieu de tenir littéralement un pointeur sur un autre objet, je stocke l'ID et je peux le rechercher via la collection masquée. Je peux utiliser le comptage de références pour traiter automatiquement les problèmes de durée de vie (sauf dans le cas de cycles disjoints, ce n’est parfois pas un problème).

Existe-t-il d'autres approches? Ou existe-t-il des bibliothèques facilitant ce genre de choses en C ++?

modifier: vous avez d'autres problèmes, tels que les relations entre les objets sont susceptibles d'être mutables dans de nombreux cas, et vous devez réfléchir à la manière dont les références aux objets doivent être stockées et à ce que des méthodes doivent être fournies pour accéder aux objets les uns des autres. Par exemple, si j'ai un descripteur sur un <=> X et que je veux représenter le concept de & "Trouver l'enfant de X nommé George &" ;, puis je dois stocker le nom & " ; George & Quot; plutôt qu'un numéro enfant: les enfants peuvent être stockés dans un vecteur et je peux éventuellement appeler X.getChildCount () et X.getChild (0), mais " George " peut ne pas toujours être l'enfant numéro 0, car d'autres enfants peuvent être insérés avant " George " dans le vecteur enfant. Ou bien X peut avoir deux, trois ou quatre autres enfants également nommés & "George &"; Ou & Quot; George & Quot; peut changer son nom en " Anthony " ou " Georgina " ;. Dans tous ces cas, il est probablement préférable d’utiliser un identifiant unique immuable.

modifier 2: (et je vais éclaircir un peu ma question une fois que tout sera clair)Je peux gérer le choix des méthodes et des noms de propriété, choisir d'utiliser une carte, une liste ou un vecteur. C'est assez simple. Les problèmes que j'essaie de traiter spécifiquement sont les suivants:

  • comment faire en sorte qu'un objet stocke une référence à un autre objet, lorsque ces objets peuvent faire partie de structures de données réallouées
  • comment gérer la gestion de la durée de vie d'un objet lorsqu'il existe des relations réciproques entre les objets
Était-ce utile?

La solution

Vous avez écrit sur le stockage d’objets de votre modèle d’objet dans std :: vector, etc., ainsi que sur les problèmes liés à l’utilisation de pointeurs sur ceux-ci. Cela me rappelle qu'il est bon de diviser vos classes C ++ en deux catégories (je ne suis pas sûr de la terminologie ici):

  1. Les classes d'entités qui représentent des objets faisant partie de votre modèle d'objet. Ils sont généralement polymorphes ou pourraient l'être à l'avenir. Ils sont créés sur le tas et sont toujours référencés par des pointeurs ou des pointeurs intelligents. Vous ne les créez jamais directement sur une pile, en tant que membres de classe / structure, ni ne les mettez directement dans des conteneurs tels que std :: vectors. Ils n'ont ni constructeur de copie ni opérateur = (vous pouvez faire une nouvelle copie avec une méthode Clone). Vous pouvez les comparer (leurs états) si cela a du sens pour vous, mais ils ne sont pas interchangeables car ils ont une identité. Chaque deux objets sont distincts.

  2. Les classes de valeurs qui implémentent des types définis par l'utilisateur (chaînes, nombres complexes, grands nombres, pointeurs intelligents, gestionnaires de wrapper, etc.). Ils sont créés directement sur la pile ou en tant que membres de classe / struct. Ils sont copiables avec copy constructeur et operator =. Ils ne sont pas polymorphes (polymorhisme et opérateur = ne fonctionnent pas bien ensemble). Vous mettez souvent leurs copies dans des conteneurs stl. Vous stockez rarement des pointeurs sur des emplacements indépendants. Ils sont interchangeables. Lorsque deux instances ont la même valeur, vous pouvez les traiter comme identiques. (Les variables qui les contiennent sont cependant différentes.)

Il existe de très bonnes raisons de ne pas respecter les règles ci-dessus. Mais j’ai observé que les ignorer dès le départ conduit à des programmes illisibles, peu fiables (surtout en matière de gestion de la mémoire) et difficiles à maintenir.

Revenons maintenant à votre question.

Si vous souhaitez stocker un modèle de données avec des relations complexes et un moyen simple d'effectuer des requêtes telles que & "Trouver l'enfant de X appelé George &", pourquoi ne pas envisager une base de données relationnelle en mémoire?

Notez que lorsque vous allez implémenter efficacement a) des relations bidirectionnelles plus complexes et des requêtes b) basées sur des propriétés d'objet différentes, vous devrez probablement créer des structures de données indexées très similaires à celles d'une base de données relationnelle. Est-ce que vos implémentations (car il y en aura beaucoup, dans un seul projet) seront vraiment plus efficaces et plus robustes?

Idem pour les " collections de tout " et identifiants d'objet. Vous devrez suivre les relations entre les objets pour éviter les identifiants sans objets. En quoi est-ce différent des pointeurs? Autres que d’obtenir des erreurs significatives au lieu de devenir fous dans toute la mémoire, c’est-à-dire ;-)

Quelques idées pour la gestion de la mémoire:

  • Propriété forte: lorsque vous pouvez déclarer qu'une entité ne vit que tant que son propriétaire et qu'il n'existe aucune possibilité de pointeurs indépendants, vous pouvez simplement la supprimer dans le destructeur du propriétaire (ou avec scoped_ptr).

  • Quelqu'un a déjà proposé smart_ptr. Ils sont parfaits et peuvent être utilisés avec des conteneurs stl. Ils sont basés sur des routeurs de référence, donc ne créez pas de cycles :-(. Je ne connais aucun pointeur automatique c ++ largement utilisé qui puisse gérer les cycles.

  • Peut-être existe-t-il un objet de niveau supérieur qui possède tous les autres objets. Par exemple. vous pouvez souvent dire que toutes les pièces appartiennent à un document, à un algorithme ou à une transaction. Ils peuvent être créés dans le contexte d'un objet de niveau supérieur, puis supprimés automatiquement lorsque leur objet de niveau supérieur est supprimé (lorsque vous supprimez un document de la mémoire ou terminez l'exécution de l'algorithme). Bien entendu, vous ne pouvez pas partager de morceaux entre des objets de niveau supérieur.

Autres conseils

Il existe environ un million d’approches (selon une estimation prudente). Vous demandez vraiment & "Comment concevoir un logiciel en C ++ &"; Et la réponse est, j'ai bien peur, & "Que va faire votre logiciel? &"; - le simple fait de savoir que vous souhaitez traiter avec des personnes et des maisons ne suffit pas.

N’est-ce pas là le but de la POO? Que ce que vous demandez est un détail d’implémentation, que vous vous cachez derrière l’interface publique de ces classes et n’ayez donc pas à vous inquiéter, car vous pouvez le changer sans changer l’interface? Alors allez-y, essayez comme vous le suggérez. Ensuite, en cas de problème de performances, de mémoire ou autre, vous pouvez corriger l’implémentation sans détruire le reste de votre code.

Il me semble que le stockage de vos données dans une base de données et l'utilisation d'une sorte de mappage relationnel-objet pourraient être une autre option à examiner.

Vous pouvez utiliser boost :: shared_ptr pour résoudre les problèmes de mémoire. Vous pouvez ensuite copier librement le shared_ptr, le retourner à partir de fonctions, l’utiliser comme variable locale, etc.

Un Person pourrait alors avoir un std::map< string, boost::shared_ptr<Person> >, de sorte que X.getChild("George") rechercherait simplement l'enfant dans la carte et renverrait le pointeur. Je pense que vous comprenez le concept, je vais donc laisser le reste comme exercice;)

Jason, ma source préférée sur le livre de la FAQ C ++ . Le problème est que vous demandez effectivement & "Comment utiliser le C ++ pour la programmation orientée objet? &";

Le mieux que je puisse dire dans une réponse à ce sujet est la suivante:

Toutes ces choses vont devenir des classes en C ++, et les relations, etc., ressembleront beaucoup à des langages à ordures auxquels vous êtes habitués: si vous avez besoin d'une relation entre une personne et son enfant nommée & george " ;, vous choisissez une structure de données pouvant stocker des personnes ou des enfants indexés par nom.

La gestion de la mémoire est en réalité plus simple qu'en C simple, si vous respectez certaines règles: assurez-vous que tous les objets qui en ont besoin possèdent des destructeurs, assurez-vous que les destructeurs nettoient tout ce que cet objet possède, puis assurez-vous de toujours les mettre objets construits de manière dynamique dans des contextes où ils sortent de la portée lorsqu'ils ne sont plus nécessaires. Ceux-ci ne couvriront pas tous les cas, mais ils vous éviteront probablement 80% des erreurs d'allocation de mémoire.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top