Evitare le dipendenze circolari dei file di intestazione

Hai qualche buon consiglio su come evitare le dipendenze circolari dei file header , per favore?

Naturalmente, fin dall’inizio, cerco di progettare il progetto nel modo più trasparente ansible. Tuttavia, man mano che vengono aggiunte sempre più funzionalità e classi e il progetto diventa meno trasparente, le dipendenze circolari iniziano a verificarsi.

Esistono regole generali, verificate e funzionanti? Grazie.

Se hai una dipendenza circolare, allora stai facendo qualcosa di sbagliato.

Ad esempio:

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

È illegale che probabilmente vuoi:

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

E questo è ok.

Regole generali:

  1. Assicurati che ciascuna intestazione possa essere inclusa da sola.
  2. Se puoi usare le dichiarazioni avanzate usali!
  • Utilizzare le dichiarazioni anticipate ove ansible.
  • Sposta qualsiasi intestazione inclusa da un file di intestazione e nel file cpp corrispondente se sono necessari solo dal file cpp. Il modo più semplice per far rispettare questo è rendere il #include "myclass.h" il primo incluso in myclass.cpp .
  • L’introduzione di interfacce nel punto di interazione tra classi separate può aiutare a ridurre le dipendenze.

Un approccio generale consiste nel tratteggiare gli elementi comuni in un terzo file di intestazione che viene quindi referenziato dai due file di intestazione originali.

Vedi anche Best Practice della Circular Dependency

Alcune buone pratiche che seguo per evitare dipendenze circolari sono,

  1. Attenersi ai principi OOAD. Non includere un file di intestazione, a meno che la class inclusa non sia in relazione di composizione con la class corrente. Utilizzare invece la dichiarazione diretta.
  2. Progettare classi astratte per fungere da interfacce per due classi. Crea l’interazione delle classi attraverso quell’interfaccia.

in base alle funzionalità del preprocessore:

 #pragma once 

o

 #ifndef MY_HEADER_H #define MY_HEADER_H your header file #endif 

Se ritieni che sia molto noioso progettare i file di intestazione, forse i creatori di Hwaci (designer di SQLite e DVC fossili) potrebbero interessarti.

Quello a cui miri è un approccio a più livelli . È ansible definire livelli in cui i moduli possono dipendere da moduli di livello inferiore, ma l’inverso dovrebbe essere eseguito con osservatori . Ora puoi ancora definire quanto dovrebbero essere grossi i tuoi strati e se accetti la dipendenza circolare all’interno dei livelli, ma in questo caso lo userei.

In generale, i file di intestazione dovrebbero dichiarare in modo diretto piuttosto che includere altre intestazioni laddove ansible.

Assicurati inoltre di attenersi a una class per intestazione.

Quindi quasi certamente non ti sbaglierai.

Il peggior accoppiamento di solito viene dal codice modello gonfiato. Poiché devi includere la definizione all’interno dell’intestazione, spesso è necessario includere intestazioni di tutti i tipi, quindi la class che utilizza il modello include l’intestazione del modello, incluso un carico di altre cose.

Per questo motivo, generalmente direi: stai attento con i modelli! Idealmente, un modello non dovrebbe includere nulla nel suo codice di implementazione.

Anche se Artyom ha fornito la migliore risposta, questo tutorial è ottimo e fornisce alcune estensioni http://www.cplusplus.com/forum/articles/10627/