web-dev-qa-db-fra.com

Éviter les dépendances circulaires des fichiers d'en-tête

Avez-vous de bons conseils sur la façon d'éviter dépendances circulaires des fichiers d'en-tête, s'il vous plaît?

Bien sûr, depuis le début, j'essaie de concevoir le projet le plus transparent possible. Cependant, à mesure que de plus en plus de fonctionnalités et de classes sont ajoutées et que le projet devient moins transparent, des dépendances circulaires commencent à se produire.

Existe-t-il des règles générales, vérifiées et fonctionnelles? Merci.

50
Bunkai.Satori

Si vous avez une dépendance circulaire, vous faites quelque chose de mal.

Comme par exemple:

foo.h
-----
class foo {
public:
   bar b;
};

bar.h
-----
class bar {
public:
   foo f;
};

Est illégal, vous voulez probablement:

foo.h
-----
class bar; // forward declaration
class foo {
   ...
   bar *b;
   ...
};

bar.h
-----
class foo; // forward declaration
class bar {
   ...
   foo *f;
   ...
};

Et c'est ok.

Règles générales:

  1. Assurez-vous que chaque en-tête peut être inclus seul.
  2. Si vous pouvez utiliser des déclarations avancées, utilisez-les!
49
Artyom
  • Utilisez des déclarations avancées dans la mesure du possible.
  • Déplacez les en-têtes inclus d'un fichier d'en-tête vers le fichier cpp correspondant s'ils ne sont nécessaires qu'au fichier cpp. Le moyen le plus simple d’appliquer ceci est de rendre le #include "myclass.h" le premier inclus dans myclass.cpp.
  • L'introduction d'interfaces au point d'interaction entre des classes distinctes peut aider à réduire les dépendances.
15
jon-hanson

Voici quelques bonnes pratiques que j'utilise pour éviter les dépendances circulaires:

  1. Respectez les principes OOAD. N'incluez pas de fichier d'en-tête, sauf si la classe incluse est en relation de composition avec la classe actuelle. Utilisez plutôt la déclaration directe.
  2. Concevez des classes abstraites pour agir en tant qu'interfaces pour deux classes. Faites l'interaction des classes via cette interface.
7
Sulla

Une approche générale consiste à factoriser les points communs dans un troisième fichier d'en-tête qui est ensuite référencé par les deux fichiers d'en-tête d'origine.

Voir aussi Meilleures pratiques en matière de dépendance circulaire

6
Ed Guiness

en fonction de vos capacités de préprocesseur:

#pragma once

ou

#ifndef MY_HEADER_H
#define MY_HEADER_H
your header file
#endif

Si vous trouvez très ennuyeux de concevoir des fichiers d'en-tête, peut-être makeheaders de Hwaci (concepteurs de SQLite et Fossil DVCS) pourrait vous intéresser.

4
Benoit

En général, les fichiers d'en-tête doivent déclarer vers l'avant plutôt que d'inclure d'autres en-têtes dans la mesure du possible.

Assurez-vous également de vous en tenir à une classe par en-tête.

Alors vous ne vous tromperez certainement pas.

Le pire couplage provient généralement du code de modèle gonflé. Parce que vous devez inclure la définition dans l'en-tête, cela conduit souvent à inclure tous les types d'en-têtes, puis la classe qui utilise le modèle inclut l'en-tête du modèle, y compris une charge d'autres éléments.

Pour cette raison, je dirais généralement: soyez prudent avec les modèles! Idéalement, un modèle ne devrait rien inclure dans son code d'implémentation.

3
CashCow

Ce que vous visez est un approche en couches. Vous pouvez définir des couches où les modules peuvent dépendre des modules des couches inférieures mais l'inverse doit être fait avec observateurs. Maintenant, vous pouvez toujours définir la granularité de vos couches et si vous acceptez la dépendance circulaire au sein des couches, mais dans ce cas, j'utiliserais this .

3
stefaanv

Altough Artyom a fourni la meilleure réponse, ce tutoriel est également excellent et fournit quelques extensions http://www.cplusplus.com/forum/articles/10627/

2
rank1