Metodi per organizzare l'interfaccia e l'implementazione in C ++

12

Ho visto che ci sono diversi paradigmi in C ++ riguardo a cosa va nel file header e cosa al file cpp. AFAIK, la maggior parte delle persone, specialmente quelle provenienti da uno sfondo C, fanno:

foo.h

 class foo {
 private:
     int mem;
     int bar();
 public:
     foo();
     foo(const foo&);
     foo& operator=(foo);
     ~foo();
 }

foo.cpp

 #include foo.h
 foo::bar() { return mem; }
 foo::foo() { mem = 42; }
 foo::foo(const foo& f) { mem = f.mem; }
 foo::operator=(foo f) { mem = f.mem; }
 foo::~foo() {}
 int main(int argc, char *argv[]) { foo f; }

Tuttavia, i miei docenti di solito insegnano C ++ ai principianti in questo modo:

foo.h

 class foo {
 private:
     int mem;
     int bar() { return mem; }
 public:
     foo() { mem = 42; }
     foo(const foo& f) { mem = f.mem; }
     foo& operator=(foo f) { mem = f.mem; }
     ~foo() {}
 }

foo.cpp

 #include foo.h
 int main(int argc, char* argv[]) { foo f; }
 // other global helper functions, DLL exports, and whatnot

Originariamente proveniente da Java, ho anche sempre seguito questo secondo modo per diverse ragioni, ad esempio che devo cambiare qualcosa in un posto solo se cambiano i nomi dell'interfaccia o del metodo, che mi piace il diverso rientro delle cose in classi quando osservo la loro implementazione e trovo i nomi più leggibili come foo rispetto a foo::foo .

Voglio raccogliere pro e contro per entrambi i modi. Forse ci sono ancora altri modi?

Uno svantaggio del mio modo di fare è ovviamente la necessità di dichiarazioni anticipate occasionali.

    
posta Felix Dombek 17.02.2011 - 11:25
fonte

4 risposte

15

Mentre la seconda versione è più facile da scrivere, è un'interfaccia di missaggio con l'implementazione.

I file di origine che includono i file di intestazione devono essere ricompilati ogni volta che i file di intestazione vengono modificati. Nella prima versione dovresti cambiare il file di intestazione solo se hai bisogno di cambiare l'interfaccia. Nella seconda versione cambieresti il file di intestazione se dovessi cambiare l'interfaccia o l'implementazione.

Inoltre, dovresti non esporre dettagli di implementazione , riceverai ricompilazione non necessaria con la seconda versione.

    
risposta data 17.02.2011 - 12:11
fonte
3

L'ho fatto al secondo posto nel '93 -95. Ci sono voluti alcuni minuti per ricompilare una piccola app con 5-10 funzioni / file (su quello stesso PC 486 .. e no, non sapevo nemmeno delle lezioni, avevo solo 14-15 anni e c'era senza internet ).

Quindi, quello che insegni ai principianti e quello che usi professionalmente sono tecniche molto diverse, specialmente in C ++.

Penso che il confronto tra C ++ e una macchina di F1 sia azzeccato. Non metti i principianti in un'auto di F1 (che non inizia nemmeno a meno che non preriscaldi il motore a 80-95 gradi centigradi).

Non insegnare il C ++ come prima lingua. Dovresti essere abbastanza esperto per sapere perché l'opzione 2 è peggio dell'opzione 1 in generale, sapere un po 'cosa significa compilazione / collegamento statico e quindi capire perché C ++ preferisce che sia la prima.

    
risposta data 17.02.2011 - 12:22
fonte
2

Il secondo metodo è quello che definirei una classe totalmente in linea. Stai scrivendo una definizione di classe, ma tutto il tuo codice che la usa codificherà il codice.

Sì, il compilatore decide quando inline e quando no ... In questo caso stai aiutando il compilatore a prendere una decisione, e potenzialmente finirai per generare meno codice e potenzialmente più veloce.

È probabile che questo vantaggio superi il fatto che se si modifica l'implementazione di una funzione è necessario ricostruire tutta la sorgente che la utilizza. Nella natura leggera della classe non modificherete l'implementazione. Se aggiungi un nuovo metodo, dovresti comunque modificare l'intestazione.

Man mano che la tua classe diventa più complessa, anche aggiungendo un ciclo, il vantaggio di farlo in questo modo diminuisce.

Ha ancora i suoi vantaggi, in particolare:

  • Se si tratta di codice "common-function", puoi semplicemente includere l'intestazione e utilizzarla da più progetti senza doverti collegare a una libreria che contiene la sua origine.

Il lato negativo dell'inlining diventa un problema quando significa che devi inserire specifiche di implementazione nell'intestazione, cioè devi iniziare a includere intestazioni extra.

Si noti che i modelli sono un caso particolare in quanto è necessario includere i dettagli dell'implementazione. Potresti oscurarlo in un altro file ma deve esserci. (Esiste un'eccezione a questa regola con le istanze, ma in generale si allineano i modelli).

    
risposta data 17.02.2011 - 14:10
fonte
1

Potrebbe non essere significativo, o vero se il tuo eseguibile diventa più grande, ma più codice nei file di intestazione consente al compilatore più possibilità di ottimizzare per la velocità.

Se sei a decidere se scrivere una libreria di sola intestazione , questo argomento è solo una delle tue preoccupazioni.

    
risposta data 27.10.2017 - 02:05
fonte

Leggi altre domande sui tag