Differenza tra "struct" e "typedef struct" in C++?




c difference between typedef struct and struct (7)

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

struct Foo { ... };

e

typedef struct { ... } Foo;

Answer #1

Una differenza importante tra una "typedef struct" e una "struct" in C ++ è che l'inizializzazione dei membri in linea in "typedef structs" 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; };

Answer #2

Struct è creare un tipo di dati. Il typedef è per impostare un soprannome per un tipo di dati.


Answer #3

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 di struct senza nome.

7.1.3 Lo specificatore typedef

1 [...]

Un nome dichiarato con lo specificatore typedef diventa un typedef-name. Nell'ambito della sua dichiarazione, un typedef-name è sintatticamente equivalente a una parola chiave e nomina il tipo associato all'identificatore nel modo descritto nella clausola 8. Un typedef-name è quindi un sinonimo per un altro tipo. Un typedef-name non introduce un nuovo tipo come fa una dichiarazione di classe (9.1) o una dichiarazione enum.

8 Se la dichiarazione typedef definisce una classe senza nome (o enum), il primo nome typedef dichiarato dalla dichiarazione come tale tipo di classe (o tipo enum) viene utilizzato per indicare il tipo di classe (o tipo enum) solo per scopi di collegamento ( 3.5). [ Esempio:

typedef struct { } *ps, S; // S is the class name for linkage purposes

Quindi, un typedef viene sempre utilizzato come segnaposto / sinonimo per un altro tipo.


Answer #4

Una differenza più importante: typedef s 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 che ne ha bisogno direttamente o meno, e così via. Può sicuramente influire sui tempi di costruzione su progetti più grandi.

Senza il typedef , in alcuni casi è sufficiente aggiungere una dichiarazione forward di struct Foo; nella parte superiore del tuo file .h , e solo #include la definizione della struct nel tuo file .cpp .


Answer #5

In C ++, c'è solo una sottile differenza. È un residuo di C, in cui fa la differenza.

Lo standard di linguaggio C ( C89 §3.1.2.3 , C99 §6.2.3 e C11 §6.2.3 ) impone namespace separati per diverse categorie di identificatori, inclusi identificatori di tag (per struct / union / enum ) e identificatori ordinari (per typedef e altri identificatori).

Se hai appena detto:

struct Foo { ... };
Foo x;

si otterrebbe un errore del compilatore, perché Foo è definito solo nello spazio dei nomi del tag.

Dovresti dichiararlo come:

struct Foo x;

Ogni volta che vuoi riferirti 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 oggetti di tipo Foo senza la parola chiave struct .

Il costrutto:

typedef struct Foo { ... } Foo;

è solo un'abbreviazione per la dichiarazione e typedef .

Finalmente,

typedef struct { ... } Foo;

dichiara una struttura anonima e crea un typedef per esso. Quindi, con questo costrutto, non ha un nome nello spazio dei nomi del tag, solo un nome nello spazio dei nomi typedef. Questo significa che non può essere inoltrato-dichiarato. Se vuoi fare una dichiarazione in avanti, devi dargli un nome nello spazio dei nomi del tag .

In C ++, tutte class dichiarazioni struct / union / enum / class comportano come se fossero implicitamente typedef , a condizione che il nome non sia nascosto da un'altra dichiarazione con lo stesso nome. Vedi la risposta di Michael Burr per tutti i dettagli.


Answer #6

Non è possibile utilizzare la dichiarazione diretta con la struttura typedef.

La struct stessa è di tipo anonimo, quindi non hai un nome effettivo da inoltrare dichiarare.

typedef struct{
    int one;
    int two;
}myStruct;

Una dichiarazione in avanti come questa non funzionerà:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

//error C2371: 'myStruct' : redefinition; different basic types

Answer #7

In questo articolo del DDJ , Dan Saks spiega una piccola area in cui i bug possono insinuarsi se non si digitano le tue strutture (e le classi!):

Se vuoi, puoi immaginare che C ++ generi un typedef per ogni nome di tag, come

typedef class string string;

Sfortunatamente, questo non è del tutto accurato. Vorrei che fosse così semplice, ma non lo è. C ++ non può generare tali typedef per structs, unions, o enum senza introdurre incompatibilità con C.

Ad esempio, supponiamo che un programma C dichiari sia una funzione sia una struct denominata status:

int status(); struct status;

Di nuovo, questo può essere una cattiva pratica, ma è C. In questo programma, lo stato (di per sé) si riferisce alla funzione; lo stato della struct si riferisce al tipo.

Se C ++ ha generato automaticamente typedef per i tag, quindi quando hai compilato questo programma come C ++, il compilatore genererebbe:

typedef struct status status;

Sfortunatamente, il nome di questo tipo sarebbe in conflitto con il nome della funzione e il programma non verrebbe compilato. Ecco perché C ++ non può semplicemente generare un typedef per ogni tag.

In C ++, i tag funzionano come i nomi typedef, tranne per il fatto che un programma può dichiarare un oggetto, una funzione o un enumeratore con lo stesso nome e lo stesso ambito di un tag. In tal caso, l'oggetto, la funzione o il nome dell'enumeratore nascondono il nome del tag. Il programma può fare riferimento al nome del tag solo utilizzando la parola chiave class, struct, union o enum (come appropriato) davanti al nome del tag. Un nome di tipo costituito da una di queste parole chiave seguite da un tag è un identificatore del tipo elaborato. Ad esempio, lo stato della struttura e il mese enum sono specificatori del tipo elaborato.

Quindi, un programma C che contiene entrambi:

int status(); struct status;

si comporta allo stesso modo quando compilato come C ++. Lo stato del nome si riferisce solo alla funzione. Il programma può riferirsi al tipo solo usando lo stato della struct-type-specific-strucifier.

Quindi, come questo permette ai bug di insinuarsi nei programmi? Considera il programma nel Listato 1 . Questo programma definisce una classe foo con un costruttore predefinito e un operatore di conversione che converte un oggetto foo in char const *. L'espressione

p = foo();

in main dovrebbe costruire un oggetto foo e applicare l'operatore di conversione. La successiva dichiarazione di output

cout << p << '\n';

dovrebbe mostrare la classe foo, ma non è così. Visualizza la funzione foo.

Questo risultato sorprendente si verifica perché il programma include l'intestazione lib.h mostrata nel Listato 2 . Questa intestazione definisce una funzione chiamata anche foo. Il nome della funzione foo nasconde il nome della classe foo, quindi il riferimento a foo in main si riferisce alla funzione, non alla classe. main può riferirsi alla classe solo usando uno specificatore di tipo elaborato, come in

p = class foo();

Il modo per evitare tale confusione in tutto il programma è aggiungere il seguente typedef per il nome della classe foo:

typedef class foo foo;

immediatamente prima o dopo la definizione della classe. Questo typedef causa un conflitto tra il nome tipo foo e il nome funzione foo (dalla libreria) che attiverà un errore in fase di compilazione.

Non conosco nessuno che scrive effettivamente questi typedef come una cosa ovvia. Richiede molta disciplina. Dal momento che l'incidenza di errori come quello del Listato 1 è probabilmente piuttosto piccola, molti di voi non si imbattono mai in questo problema. Ma se un errore nel tuo software potrebbe causare lesioni personali, dovresti scrivere i typedef, non importa quanto improbabile sia l'errore.

Non riesco a immaginare perché qualcuno vorrebbe mai nascondere un nome di classe con una funzione o un nome di oggetto nello stesso ambito della classe. Le regole nascoste in C erano un errore e non avrebbero dovuto essere estese alle classi in C ++. In effetti, puoi correggere l'errore, ma richiede una disciplina di programmazione e uno sforzo extra che non dovrebbero essere necessari.





typedef