Differenza tra 'struct' e 'typedef struct' in C ++?
Domanda
In C ++, c'è qualche differenza tra:
struct Foo { ... };
e
typedef struct { ... } Foo;
Soluzione
In C ++, c'è solo una sottile differenza. È un holdover da C, in cui fa la differenza.
Lo standard del linguaggio C ( C89 §3.1.2.3 , C99 §6.2.3 e C11 §6.2.3 ) impone spazi dei nomi separati per diverse categorie di identificatori, tra cui < em> identificatori di tag (per struct
/ union
/ enum
) e identificatori ordinari (per < code> typedef e altri identificatori).
Se hai appena detto:
struct Foo { ... };
Foo x;
visualizzerai un errore del compilatore, poiché Foo
è definito solo nello spazio dei nomi dei tag.
Dovresti dichiararlo come:
struct Foo x;
Ogni volta che vuoi fare riferimento a un Foo
, dovresti sempre chiamarlo struct Foo
. Questo diventa fastidioso velocemente, quindi puoi aggiungere un typedef
:
struct Foo { ... };
typedef struct Foo Foo;
Ora struct Foo
(nello spazio dei nomi dei tag) e semplicemente Foo
(nello spazio dei nomi dell'identificatore ordinario) si riferiscono entrambi alla stessa cosa e puoi dichiarare liberamente gli oggetti di tipo Foo
senza la parola chiave struct
.
Il costrutto:
typedef struct Foo { ... } Foo;
è solo un'abbreviazione per la dichiarazione e typedef
.
Infine,
typedef struct { ... } Foo;
dichiara una struttura anonima e crea un typedef
per essa. Pertanto, con questo costrutto, non ha un nome nello spazio dei nomi dei tag, ma solo un nome nello spazio dei nomi typedef. Ciò significa che non può essere dichiarato in avanti. Se vuoi fare una dichiarazione in avanti, devi dargli un nome nello spazio dei nomi dei tag .
In C ++, tutte le dichiarazioni struct
/ union
/ enum
/ class
agiscono come se fossero implicitamente
Altri suggerimenti
In questo articolo del DDJ , Dan Saks spiega una piccola area in cui i bug possono insinuarsi se non digiti le tue strutture (e classi!):
Se vuoi, puoi immaginare quel C ++ genera un typedef per ogni tag nome, come
typedef class string string;
Sfortunatamente, questo non è del tutto accurate. Vorrei che fosse così semplice, ma non lo è. C ++ non può generare tale typedefs per strutture, sindacati o enumerazioni senza introdurre incompatibilità con C.
Ad esempio, supponiamo che un programma C. dichiara sia una funzione che una struttura stato denominato:
int status(); struct status;
Ancora una volta, questa potrebbe essere una cattiva pratica, ma è C. In questo programma, lo stato (di stesso) si riferisce alla funzione; struct lo stato si riferisce al tipo.
Se C ++ è stato generato automaticamente typedefs per i tag, quindi quando tu compilato questo programma come C ++, il il compilatore genererebbe:
typedef struct status status;
Sfortunatamente, questo nome di tipo lo farebbe è in conflitto con il nome della funzione e il programma non si sarebbe compilato. Quello è perché C ++ non può semplicemente generare un typedef per ogni tag.
In C ++, i tag si comportano proprio come typedef nomi, tranne che un programma può dichiarare un oggetto, una funzione o enumeratore con lo stesso nome e il stesso ambito di un tag. In tal caso, il oggetto, funzione o nome dell'enumeratore nasconde il nome del tag. Il programma può fare riferimento al nome della tag solo usando la parola chiave class, struct, union o enum (a seconda dei casi) di fronte al nome del tag. Un nome di tipo composto da una di queste parole chiave seguita da a tag è un identificatore di tipo elaborato. Ad esempio, struct status ed enum i mesi sono specificatori di tipo elaborato.
Quindi, un programma C che contiene entrambi:
p = foo();
si comporta allo stesso modo quando compilato come C ++. Il solo stato del nome si riferisce al funzione. Il programma può fare riferimento a digitare solo usando il struct elaboratore-tipo-specificatore di stato.
Quindi, in che modo questo consente ai bug di insinuarsi nei programmi? Considera il programma in Annuncio 1 . Questo programma definisce a classe pippo con un costruttore predefinito, e un operatore di conversione che converte un oggetto foo in char const *. L'espressione
cout << p << '\n';
in main dovrebbe costruire un oggetto foo e applica l'operatore di conversione. Il istruzione di output successiva
p = class foo();
dovrebbe visualizzare la classe foo, ma esso non lo fa. Visualizza la funzione foo.
Questo risultato sorprendente si verifica perché il programma include l'intestazione lib.h mostrato in Listato 2 . Questa intestazione definisce una funzione chiamata anche foo. Il nome funzione foo nasconde il nome della classe pippo, quindi il riferimento a pippo in principale si riferisce alla funzione, non alla classe. main può riferirsi alla classe solo da usando un identificatore di tipo elaborato, come in
typedef class foo foo;
Il modo per evitare tale confusione in tutto il programma è aggiungere il seguente typedef per il nome della classe pippo:
<*>immediatamente prima o dopo la lezione definizione. Questo errore di battitura causa a conflitto tra il nome del tipo foo e il nome della funzione foo (dal libreria) che attiverà a errore di compilazione.
Non conosco nessuno che scriva davvero questi dattiloscritti ovviamente. Richiede molta disciplina. Da l'incidenza di errori come il uno in Annuncio 1 è probabilmente carino piccolo, molti non si scontrano mai questo problema. Ma se un errore nel tuo il software potrebbe causare lesioni personali, allora dovresti scrivere il typedefs no importa quanto sia improbabile l'errore.
Non riesco a immaginare perché qualcuno lo abbia mai fatto vuoi nascondere un nome di classe con a nome della funzione o dell'oggetto nello stesso ambito come la classe. Le regole per nascondere in C sono stati un errore, e dovrebbero non sono stati estesi alle classi in C ++. In effetti, puoi correggere il errore, ma richiede extra disciplina di programmazione e sforzo che non dovrebbe essere necessario.
Un'altra importante differenza: typedef
non può essere dichiarato in avanti. Quindi per l'opzione typedef
devi #include
il file contenente typedef
, che significa tutto ciò che #include
è il tuo .h
include anche quel file indipendentemente dal fatto che ne abbia bisogno o meno, e così via. Può sicuramente influire sui tempi di costruzione su progetti più grandi.
Senza il typedef
, in alcuni casi puoi semplicemente aggiungere una dichiarazione in avanti di struct Foo;
nella parte superiore del tuo file .h
e solo #include
la definizione della struttura nel file .cpp
.
C'è una differenza, ma sottile. Guardalo in questo modo: struct Foo
introduce un nuovo tipo. Il secondo crea un alias chiamato Foo (e non un nuovo tipo) per un tipo struct
senza nome.
7.1.3 L'identificatore typedef
1 [...]
Un nome dichiarato con l'identificatore typedef diventa un nome typedef. Nell'ambito della sua dichiarazione, a typedef-name è sintatticamente equivalente a una parola chiave e nomina il tipo associato all'identificatore in il modo descritto nella Clausola 8. Un typedef-name è quindi sinonimo di un altro tipo. Un typedef-name non introduce un nuovo tipo come fa una dichiarazione di classe (9.1) o enum.
8 Se la dichiarazione typedef definisce una classe (o enum) senza nome, il primo nome typedef dichiarato dalla dichiarazione essere quel tipo di classe (o tipo enum) viene usato per indicare il tipo di classe (o tipo enum) per il collegamento solo per scopi (3.5). [Esempio:
typedef struct { } *ps, S; // S is the class name for linkage purposes
Quindi, un typedef sempre viene utilizzato come segnaposto / sinonimo per un altro tipo.
Non puoi usare la dichiarazione diretta con la typedef struct.
La struttura stessa è di tipo anonimo, quindi non hai un nome reale da dichiarare in avanti.
typedef struct{
int one;
int two;
}myStruct;
Una dichiarazione a termine come questa non funzionerà:
struct myStruct; //forward declaration fails
void blah(myStruct* pStruct);
//error C2371: 'myStruct' : redefinition; different basic types
Struct è creare un tipo di dati. Il typedef consiste nell'impostare un soprannome per un tipo di dati.
Una differenza importante tra una "typedef struct" e una "struct" in C ++ è che l'inizializzazione dei membri inline in "typedef struct" non funzionerà.
// the 'x' in this struct will NOT be initialised to zero
typedef struct { int x = 0; } Foo;
// the 'x' in this struct WILL be initialised to zero
struct Foo { int x = 0; };
Non c'è alcuna differenza in C ++, ma credo in C che ti consentirebbe di dichiarare istanze della struttura Foo senza fare esplicitamente:
struct Foo bar;