Comprese le intestazioni all’interno della dichiarazione / definizione della class

So che puoi fare qualcosa come:

def.h:

A(); int x; 

ah

 class A { public: #include "def.h" } 

A.cpp

 A::A() { x = 0; } int main() { A a; return 0; } 

Le mie domande sono: perché lo faresti? Ci sono dei vantaggi? Posso vedere come sarebbe utile se hai alcune classi con gli stessi membri ma non la stessa base, ma ne vale la pena? Non è molto leggibile, vero? Inoltre, come tratta il compilatore questi include? Fa semplicemente incollare il contenuto dell’intestazione in cui è incluso (un po ‘come una macro)?

Il preprocessore (che viene eseguito prima di tutto ), quando si imbatte in un include , copia quasi letteralmente il contenuto di quell’intestazione e lo incolla al posto della direttiva #include .

I vantaggi di usarlo come descrivi sono pochi, il principale è che non devi duplicare il codice.

Tuttavia, in 9999/10000 situazioni, non ne vale sicuramente la pena. Se hai un errore di battitura da qualche parte nel file di intestazione, riceverai strani errori in ogni file che lo utilizza, e non è chiaro a tutti che cosa stia facendo fino a quando non apri effettivamente il file e lo leggi.

Evitarlo se ansible. Non riesco a pensare a una situazione in cui sarebbe assolutamente necessario; lo stesso effetto può essere ottenuto con l’ereditarietà o la composizione il più delle volte senza tutti gli effetti collaterali.

Non l’ho mai visto in una class e ti consiglierei di non farlo mai se vuoi ancora capire il codice l’altro giorno.

Detto questo, c’è un caso in cui trovo questa tecnica accettabile e cioè quando si ha una grande tabella da cui è necessario generare più costrutti come un enum e una tabella degli attributi. Diamo due file come:

foobars.h:

 enum Foobars { #define FOOBAR(id, description, args) FOOBAR_##id, #include "foobars.tab" #undef FOOBAR }; extern const char *foobar_names[]; const char *parse_foobar(Foobars fb, const char *input); 

foobars.cpp:

 #include "foobars.h" const char *foobar_names[] = { #define FOOBAR(id, description, args) description, #include "foobars.tab" #undef FOOBAR }; const char *parse_foobar(Foobars fb, const char *input) { switch(fb) { #define INT get_int(&input) #define FLOAT get_float(&input) #define STRING get_string(&input) #define FOOBAR(id, description, args) args #include "foobars.tab" #undef FOOBAR } return input; 

E la magia è in “foobars.tab” (è speciale, quindi mi raccomando di non chiamare niente.h o niente.hpp o qualsiasi altro suffisso comune):

 /* CAUTION! This file is included using C preprocessor in the middle of various structures * It must not contain anything except definitions of foobars in the FOOBAR macro and * comments. Don't forget NOT to write semicolons; some of the structures are * comma-separated and some semicolon-separated. FOOBAR will be defined appropriately before * including this file. */ FOOBAR(NULL, "Empty command, does nothing", {}) // NO semicolon! // Also some preprocessors don't like empty arguments, so that's why {}. // (void)0 is an alternative. FOOBAR(FOO, "Foo bars and bazes", a = INT; b = STRING) FOOBAR(BAR, "Bars, but does not baz", x = FLOAT) ... 

L’altra opzione è la definizione di una macro per il contenuto dell’offerta speciale. Se la tabella è breve, la macro è più facile da leggere, ma se il file è lungo, il file speciale ha più senso.

L’ultima opzione è quella di avere la tabella in un formato completamente diverso e generare il codice, ma ciò comporta la scrittura di alcuni script speciali per costruirlo e questo no.

In lingue come Ruby questo concetto è noto come Mixin . Dal momento che abbiamo ereditarietà multipla in C ++, non ne abbiamo bisogno qui.

Un utilizzo che ho trovato è che se si desidera generare automaticamente un numero enorme di righe nella definizione della class, potrebbe essere utile includere il file generato automaticamente come questo.

Queste risposte sono tutte molto vecchie ma ho trovato una ragione per farlo che non è elencato sopra. Sto scrivendo test automatici che richiedono l’accesso a membri privati ​​e quindi l’uso di dichiarazioni di amicizia in molte classi. Dal momento che l’amicizia non è ereditata, devo dichiarare esplicitamente l’amicizia di ogni nuova class di test in ogni class con cui interagisce.

Questo è molto più facile da fare se ci sono uno o più file che elencano le mie classi di test come per “test_friends.h”:

 friend class testOneFeature; friend class testAnotherFeature; 

ecc. e nelle classi testate posso semplicemente includere quel file nella dichiarazione.

 class MyClass { #include "test_friends.h" public: //etc };