Question Différence entre 'struct' et 'typedef struct' dans C ++?


En C ++, y a-t-il une différence entre:

struct Foo { ... };

et

typedef struct { ... } Foo;

675
2018-03-04 20:41


origine


Réponses:


En C ++, il n'y a qu'une différence subtile. C'est un holdover de C, dans lequel cela fait la différence.

La norme de langage C (C89 §3.1.2.3, C99 §6.2.3, et C11 §6.2.3) impose des espaces de noms distincts pour différentes catégories d'identificateurs, y compris identificateurs de balises (pour struct/union/enum) et identifiants ordinaires (pour typedef et d'autres identifiants).

Si vous venez de dire:

struct Foo { ... };
Foo x;

vous obtiendrez une erreur de compilation, car Foo est seulement défini dans l'espace de noms de balise.

Vous devez le déclarer comme:

struct Foo x;

Chaque fois que vous voulez vous référer à un Foo, vous devez toujours l'appeler un struct Foo. Cela devient ennuyeux rapidement, de sorte que vous pouvez ajouter un typedef:

struct Foo { ... };
typedef struct Foo Foo;

À présent struct Foo (dans l'espace de noms de balise) et tout simplement Foo (dans l'espace de noms des identifiants ordinaires) se réfèrent tous les deux à la même chose, et vous pouvez déclarer librement les objets de type Foo sans le struct mot-clé.


La construction:

typedef struct Foo { ... } Foo;

est juste une abréviation pour la déclaration et typedef.


Finalement,

typedef struct { ... } Foo;

déclare une structure anonyme et crée un typedef pour ça. Ainsi, avec cette construction, il n'y a pas de nom dans l'espace de noms des balises, seulement un nom dans l'espace de noms typedef. Cela signifie qu'il ne peut pas non plus être déclaré à l'avance. Si vous voulez faire une déclaration avant, vous devez lui donner un nom dans l'espace de noms des balises.


En C ++, tous struct/union/enum/class les déclarations agissent comme si elles sont implicitement typedef'ed, tant que le nom n'est pas caché par une autre déclaration avec le même nom. Voir La réponse de Michael Burr pour tous les détails.


1018
2018-03-04 20:45



Dans cet article DDJ, Dan Saks explique une petite zone où les bugs peuvent se faufiler si vous ne typez pas vos structures (et classes!):

Si vous voulez, vous pouvez imaginer que C ++   génère un typedef pour chaque tag   nom, tel que

typedef class string string;

Malheureusement, ce n'est pas entièrement   précis. J'aimerais que ce soit aussi simple,   mais ce n'est pas. C ++ ne peut pas générer de tels   typedefs pour les structures, les unions ou les enums   sans introduire d'incompatibilités   avec C.

Par exemple, supposons un programme C   déclare à la fois une fonction et une structure   statut nommé:

int status(); struct status;

Encore une fois, cela peut être une mauvaise pratique, mais   c'est C. Dans ce programme, status (par   lui-même) se réfère à la fonction; struct   l'état fait référence au type.

Si C ++ a généré automatiquement   typedefs pour les étiquettes, puis quand vous   compilé ce programme en C ++, le   le compilateur générerait:

typedef struct status status;

Malheureusement, ce nom de type serait   entrer en conflit avec le nom de la fonction, et   le programme ne compilerait pas. C'est   pourquoi C ++ ne peut pas simplement générer un   typedef pour chaque tag.

En C ++, les tags agissent comme typedef   noms, sauf qu'un programme peut   déclarer un objet, une fonction ou   énumérateur avec le même nom et le   même portée qu'une étiquette. Dans ce cas, le   objet, fonction ou nom de l'énumérateur   masque le nom du tag. Le programme peut   se référer au nom du tag uniquement en utilisant   le mot clé class, struct, union ou   enum (le cas échéant) devant le   nom du tag. Un nom de type composé de   un de ces mots-clés suivi d'un   tag est un spécificateur de type élaboré.   Par exemple, struct status et enum   mois sont des spécificateurs de type élaborés.

Ainsi, un programme C qui contient à la fois:

int status(); struct status;

se comporte de la même manière lorsqu'il est compilé en C ++.   Le statut du nom fait référence à la   fonction. Le programme peut se référer à   tapez seulement en utilisant le   structure de spécificateur-type-spécificateur   statut.

Alors, comment cela permet aux bogues de se glisser   dans les programmes? Considérez le programme dans    Listing 1. Ce programme définit un   classe foo avec un constructeur par défaut,   et un opérateur de conversion   convertit un objet foo en char const *.   L'expression

p = foo();

en main devrait construire un objet foo   et appliquez l'opérateur de conversion. le   déclaration de sortie suivante

cout << p << '\n';

devrait afficher la classe foo, mais il   ne le fait pas. Il affiche la fonction foo.

Ce résultat surprenant se produit parce que   le programme inclut l'en-tête lib.h   montré dans Listing 2. Cet en-tête   définit une fonction également nommée foo. le   nom de la fonction foo masque le nom de la classe   foo, donc la référence à foo dans main   fait référence à la fonction, pas à la classe.   principal peut se référer à la classe seulement par   en utilisant un spécificateur de type élaboré, comme   dans

p = class foo();

Le moyen d'éviter une telle confusion   tout au long du programme est d'ajouter le   typedef suivant pour le nom de la classe   foo:

typedef class foo foo;

immédiatement avant ou après la classe   définition. Ce typedef provoque un   conflit entre le nom de type foo et   le nom de la fonction foo (du   bibliothèque) qui déclenchera une   erreur de compilation.

Je ne connais personne qui écrit réellement   ces typedefs comme une évidence.   Cela demande beaucoup de discipline. Depuis   l'incidence des erreurs telles que le   un dans Listing 1 est probablement jolie   petit, vous ne courez jamais   ce problème. Mais si une erreur dans votre   le logiciel pourrait causer des blessures corporelles,   alors vous devriez écrire le typedefs pas   importe comment improbable l'erreur.

Je ne peux pas imaginer pourquoi quelqu'un aurait jamais   vouloir cacher un nom de classe avec un   nom de fonction ou d'objet dans le même   portée comme la classe. Les règles de dissimulation   en C étaient une erreur, et ils devraient   pas été étendu aux classes de   C ++. En effet, vous pouvez corriger le   erreur, mais il faut plus   la discipline de programmation et l'effort que   ne devrait pas être nécessaire.


203
2018-03-04 21:19



Une différence plus importante: typedefs ne peut pas être déclaré en avant. Donc pour le typedef option que vous devez #include le fichier contenant le typedef, ce qui signifie tout ce qui #includes votre .h inclut également ce fichier, qu'il en ait besoin directement ou non, et ainsi de suite. Cela peut certainement avoir un impact sur vos temps de construction sur des projets plus importants.

Sans le typedef, dans certains cas, vous pouvez simplement ajouter une déclaration de struct Foo; au sommet de votre .h fichier, et seulement #include la définition de struct dans votre .cpp fichier.


57
2018-03-04 20:54



est une différence, mais subtile. Vois-le de cette façon: struct Foo introduit un nouveau type. Le second crée un alias appelé Foo (et non un nouveau type) pour un nom sans nom struct type.

7.1.3 Le spécificateur typedef

1 [...]

Un nom déclaré avec le spécificateur typedef devient un typedef-name. Dans le cadre de sa déclaration, un   typedef-name est syntactiquement équivalent à un mot-clé et nomme le type associé à l'identifiant   de la manière décrite à l'Article 8. Un nom de typedef est donc synonyme d'un autre type. Un nom de typedef ne présente pas un nouveau type comme le fait une déclaration de classe (9.1) ou une déclaration enum.

8 Si la déclaration typedef définit une classe sans nom (ou enum), le premier nom de typedef déclaré par la déclaration   être ce type de classe (ou type enum) est utilisé pour désigner le type de classe (ou le type enum) pour la liaison   fins seulement (3.5). [ Exemple:

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

Donc, un typedef toujours est utilisé comme un espace réservé / synonyme pour un autre type.


28
2018-03-04 20:49



Vous ne pouvez pas utiliser la déclaration forward avec la structure typedef.

La structure elle-même est de type anonyme, vous n'avez donc pas de nom réel à transmettre.

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

Une déclaration avant comme ceci ne fonctionnera pas:

struct myStruct; //forward declaration fails

void blah(myStruct* pStruct);

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

10
2017-08-05 09:18



Une différence importante entre un 'typedef struct' et un 'struct' en C ++ est que l'initialisation des membres inline dans 'typedef structs' ne fonctionnera pas.

// 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; };

0
2018-04-11 13:26



Struct est de créer un type de données. Le typedef consiste à définir un pseudonyme pour un type de données.


-1
2018-04-08 03:06



Il n'y a pas de différence en C ++, mais je crois en C cela vous permettrait de déclarer des instances de la structure Foo sans faire explicitement:

struct Foo bar;

-2
2018-03-04 20:44