Domanda

In C ++, c'è qualche differenza tra:

struct Foo { ... };

e

typedef struct { ... } Foo;
È stato utile?

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 typedef 'ed, purché il nome non sia nascosto da un'altra dichiarazione con lo stesso nome. Vedi la risposta di Michael Burr per i dettagli completi .

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;
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top