Question

J'ai appris à l'université que vous devez toujours libérer vos objets inutilisés, mais pas comment vous le faites réellement.Par exemple, structurer correctement votre code, etc.Existe-t-il des règles générales sur la façon de gérer les pointeurs en C++ ?

Je ne suis actuellement pas autorisé à utiliser Boost.Je dois m'en tenir au C++ pur car le framework que j'utilise interdit toute utilisation de génériques.

Était-ce utile?

La solution

J'ai travaillé avec le système d'exploitation Symbian intégré, qui disposait d'un excellent système pour cela, entièrement basé sur les conventions des développeurs.

  1. Un seul objet possédera un pointeur.Par défaut, c'est le créateur.
  2. La propriété peut être transmise.Pour indiquer le transfert de propriété, l'objet est passé sous forme de pointeur dans la signature de la méthode (par ex.void Foo(Bar *zonk);).
  3. Le propriétaire décidera quand supprimer l'objet.
  4. Pour passer un objet à une méthode juste pour l'utiliser, l'objet est passé comme référence dans la signature de la méthode (par ex.void Foo(Bat &zonk);).
  5. Les classes non-propriétaires peuvent stocker des références (jamais des pointeurs) aux objets qui leur sont donnés uniquement lorsqu'elles peuvent être sûres que le propriétaire ne les détruira pas pendant leur utilisation.

Fondamentalement, si une classe utilise simplement quelque chose, elle utilise une référence.Si une classe possède quelque chose, elle utilise un pointeur.

Cela a fonctionné à merveille et a été un plaisir à utiliser.Les problèmes de mémoire étaient très rares.

Autres conseils

Règles:

  1. Dans la mesure du possible, utilisez unpointeur intelligent.Boost en abons.
  2. Si vous ne pouvez pas utiliser un pointeur intelligent, Null Out votre pointeur après l'avoir supprimé.
  3. Ne travaillez jamais dans un endroit qui ne vous permettra pas d'utiliser la règle 1.

Si quelqu'un interdit la règle 1, rappelez-vous que si vous récupérez le code de quelqu'un d'autre, modifiez les noms des variables et supprimez les mentions de droits d'auteur, personne ne le remarquera jamais.À moins qu'il ne s'agisse d'un projet scolaire, où ils vérifient ce genre de manigances avec des outils assez sophistiqués.Voir également, cette question.

J'ajouterais une autre règle ici :

  • Ne créez/supprimez pas un objet lorsqu'un objet automatique fera très bien l'affaire.

Nous avons constaté que les programmeurs qui débutent en C++, ou les programmeurs issus de langages comme Java, semblent apprendre de nouvelles choses et les utilisent ensuite de manière obsessionnelle chaque fois qu'ils souhaitent créer un objet, quel que soit le contexte.Ceci est particulièrement pernicieux lorsqu'un objet est créé localement au sein d'une fonction uniquement pour faire quelque chose d'utile.Utiliser new de cette manière peut nuire aux performances et rendre trop facile l'introduction de fuites de mémoire stupides lorsque la suppression correspondante est oubliée.Oui, les pointeurs intelligents peuvent aider dans ce dernier cas, mais cela ne résoudra pas les problèmes de performances (en supposant que new/delete ou un équivalent soit utilisé en coulisses).Fait intéressant (enfin, peut-être), nous avons constaté que la suppression a souvent tendance à être plus coûteuse que la nouvelle lors de l'utilisation de Visual C++.

Une partie de cette confusion vient également du fait que les fonctions qu'elles appellent peuvent prendre des pointeurs, voire des pointeurs intelligents, comme arguments (alors que les références seraient peut-être meilleures/plus claires).Cela leur fait penser qu'ils doivent "créer" un pointeur (beaucoup de gens semblent penser que c'est ce que fait new) pour pouvoir passer un pointeur vers une fonction.De toute évidence, cela nécessite certaines règles sur la façon dont les API sont écrites pour rendre les conventions d'appel aussi claires que possible, qui sont renforcées par des commentaires clairs fournis avec le prototype de fonction.

Dans le cas général (gestion des ressources, où la ressource n'est pas nécessairement la mémoire), il faut connaître le modèle RAII.C’est l’une des informations les plus importantes pour les développeurs C++.

En général, évitez d'allouer à partir du tas, sauf si vous y êtes obligé.Si nécessaire, utilisez le comptage de références pour les objets qui ont une durée de vie longue et qui doivent être partagés entre diverses parties de votre code.

Parfois, vous devez attribuer des objets de manière dynamique, mais ils ne seront utilisés que pendant un certain laps de temps.Par exemple, dans un projet précédent, j'avais besoin de créer une représentation complexe en mémoire d'un schéma de base de données - essentiellement un graphe cyclique complexe d'objets.Cependant, le graphique n’était nécessaire que pendant la durée d’une connexion à la base de données, après quoi tous les nœuds pouvaient être libérés d’un seul coup.Dans ce type de scénario, un bon modèle à utiliser est quelque chose que j'appelle "l'idiome GC local". Je ne sais pas s'il a un nom "officiel", car c'est quelque chose que je n'ai vu que dans mon propre code, et dans Cocoa (voir NSAutoreleasePool dans la référence Cocoa d'Apple).

En un mot, vous créez un objet « collecteur » qui conserve des pointeurs vers les objets temporaires que vous allouez à l’aide de new.Il est généralement lié à une certaine portée de votre programme, soit une portée statique (par ex.-- en tant qu'objet alloué à la pile qui implémente l'idiome RAII) ou dynamique (par ex.-- lié à la durée de vie d'une connexion à une base de données, comme dans mon projet précédent).Lorsque l'objet "collecteur" est libéré, son destructeur libère tous les objets vers lesquels il pointe.

De plus, comme DrPizza, je pense que la restriction de ne pas utiliser de modèles est trop stricte.Cependant, ayant fait beaucoup de développement sur les anciennes versions de Solaris, AIX et HP-UX (tout récemment - oui, ces plates-formes sont toujours présentes dans le Fortune 50), je peux vous dire que si vous vous souciez vraiment de la portabilité, vous devrait utiliser des modèles le moins possible.Les utiliser pour des conteneurs et des pointeurs intelligents devrait cependant être acceptable (cela a fonctionné pour moi).Sans modèles, la technique que j'ai décrite est plus pénible à mettre en œuvre.Cela nécessiterait que tous les objets gérés par le « collecteur » dérivent d’une classe de base commune.

Bonjour,

Je suggère de lire les sections pertinentes de « Effective C++ » de Scott Meyers.Facile à lire et il couvre quelques pièges intéressants pour piéger les imprudents.

Je suis également intrigué par le manque de modèles.Donc pas de STL ni de Boost.Ouah.

BTW Amener les gens à se mettre d'accord sur des conventions est une excellente idée.Tout comme amener tout le monde à se mettre d’accord sur les conventions pour l’OOD.BTW La dernière édition d'Effective C++ ne contient pas l'excellent chapitre sur les conventions OOD de la première édition, ce qui est dommage, par ex.conventions telles que l'héritage virtuel public toujours modélise une relation « isa ».

Rob

  • Lorsque vous devez utiliser la mémoire manuellement, assurez-vous d'appeler Delete dans la même portée / fonction / classe / module, qui s'applique jamais en premier, par exemple:
  • Laissez l'appelant d'une fonction allouer la mémoire qui en est remplie, ne renvoyez pas de pointeurs nouveaux.
  • Appelez toujours delete dans le même exe/dll que celui dans lequel vous avez appelé new, car sinon vous pourriez avoir des problèmes de corruption de tas (différentes bibliothèques d'exécution incompatibles).

vous pouvez tout dériver d'une classe de base qui implémente une fonctionnalité de type pointeur intelligent (en utilisant les méthodes ref()/unref() et un compteur.

Tous les points soulignés par @Timbo sont importants lors de la conception de cette classe de base.

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