2011-01-27 24 views
47

Avete qualche buon consiglio su come evitare le dipendenze circolari dei file di intestazione, per favore?Evitare le dipendenze circolari dei file di intestazione

Naturalmente, sin dall'inizio, cerco di progettare il progetto nel modo più trasparente possibile. 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.

risposta

48

Se si dispone di dipendenza circolare, allora stai facendo qualcosa di sbagliato.

Come per esempio:

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

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

è illegale probabilmente si desidera:

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. assicurarsi che ogni intestazione può essere incluso da solo.
  2. Se è possibile utilizzare le dichiarazioni di inoltro usarle!
+0

+1 Ciao Artyom, grazie per la risposta. un utilizzo più frequente delle dichiarazioni anticipate potrebbe essere utile. –

+0

@Artyom: se il puntatore è il proprietario della risorsa, suggerirei di usare un 'scoped_ptr' o' unique_ptr'. Se il puntatore è semplicemente un riferimento a un oggetto, potrebbe essere necessario utilizzare un pattern Observer in modo che sia "non impostato" ogni volta che l'oggetto di riferimento viene distrutto. –

+0

@Matthieu M. Ovviamente, (o 'auto_ptr', che è meglio quando non si vuole dipendere da' boost' o 'C++ 0x'). Ma volevo piuttosto mostrare un'idea generale piuttosto codice dal vivo. Potrebbe anche essere 'std :: vector ' che funzionerebbe se foo avesse una dichiarazione in avanti. – Artyom

4

a seconda delle capacità del preprocessore:

#pragma once 

o

#ifndef MY_HEADER_H 
#define MY_HEADER_H 
your header file 
#endif 

Se lo trovate molto noioso per la progettazione di file di intestazione forse makeheaders da Hwaci (progettisti di SQLite e DVCS fossili) potrebbe essere di interesse per te.

+2

Questo non è tanto per evitare dipendenze circolari, da evitare "ridefinizione del simbolo" errori. È comunque una pratica standard, assolutamente necessaria. –

+0

Ciao Benoid, si, sono d'accordo con Peter Torok. Questo qualcosa è spiegato in ogni libro di testo e in un pratcice obbligatorio. Molte grazie per la tua risposta. –

6

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 Circular Dependency Best Practice

+0

+1 Ciao Ed, questo è un altro ottimo consiglio. Grazie. –

+0

Ho controllato il collegamento che hai fornito e mostra un bell'esempio di riprogettazione in classe per evitare dipendenze circolari. –

+0

Grazie, mi è piaciuto anche ;-) –

3

Quello che stai puntando a è un layered approach. È possibile definire i livelli in cui i moduli possono dipendere da moduli di livello inferiore, ma l'inverso deve essere eseguito con observers. Ora puoi ancora definire quanto dovrebbero essere grani i tuoi strati e se accetti la dipendenza circolare all'interno dei livelli, ma in questo caso userei lo this.

+0

+1 ciao Stefaanv, l'approccio a strati è abbastanza nuovo per me e sembra qualcosa che richiede molti preparativi e riprogettazione. È un consiglio molto prezioso. Grazie. –

+0

L'approccio a strati è una grande idea, in particolare perché non è specifica per C++ ed è quindi utile in molte situazioni :) –

15
  • Utilizzare dichiarazioni di inoltro ove possibile.
  • 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 ciò è rendere #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.
+1

+1 Ciao Jon, grazie per la tua risposta. Alcuni dei tuoi consigli sono già stati menzionati sopra, ma quello di includere sempre # include i file heade in file .cpp. I file .h erano nuovi e utili. –

+0

Penso che questa risposta affronti meglio la domanda su come evitare errori di compilazione con dipendenze circolari evitando il mantra che hai fatto qualcosa di sbagliato perché devi fare i conti con una dipendenza circolare. Se lavori con schemi di progettazione e complessità di GoF, a un certo punto avrai una dipendenza circolare. Il miglior consiglio non è solo una dichiarazione diretta (che semplifica eccessivamente la soluzione), ma il punto n. 2. –

+0

Il secondo suggerimento è quello che stavo cercando – rluks

3

In generale, i file di intestazione devono dichiarare in modo diretto anziché includere altre intestazioni laddove possibile.

Assicurarsi inoltre di attenersi a una classe per intestazione.

Quindi quasi certamente non andrà male.

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

Per questo motivo, in genere direi: fai attenzione ai modelli! Idealmente, un modello non dovrebbe includere nulla nel suo codice di implementazione.

+0

+1 Hi CashCow, ad essere sincero, non ho prestato troppa attenzione alle dichiarazioni di inoltro. Invece ho usato #include. Grazie mille, per questa risposta. –

6

Alcune buone pratiche che seguo per evitare dipendenze circolari sono,

  1. Stick ai principi OOAD. Non includere un file di intestazione, a meno che la classe inclusa non sia in relazione di composizione con la classe 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.
+0

+1 hey Arun, in particolare il secondo consiglio di utilizzare le classi di astrazione/interfaccia è stato utile. Farò un tentativo. Grazie. –

Problemi correlati